C 和指针 1 至 4 章笔记

第 1 章 快速上手

主要通过讲解一个例子,来让读者对 C 有一个大概印象。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_COLS 20
#define MAX_INPUT 1000

int read_column_numbers(int columns[], int max);
void rearrange(char *output, char const *input, int n_columns, int const columns[]);

int main(void)
{
    int n_columns;
    int columns[MAX_COLS];
    char input[MAX_INPUT];
    char output[MAX_INPUT];

    n_columns = read_column_numbers(columns, MAX_COLS);

    //while (gets(input) != NULL) {
    while (fgets(input, MAX_INPUT, stdin) != NULL) {
        printf("Original input : %s\n", input);
        rearrange(output, input, n_columns, columns);
        printf("Rearranged line: %s\n", output);
    }

    return EXIT_SUCCESS;
}

int read_column_numbers(int columns[], int max)
{
    int num=0;
    int ch;

    while (num < max && scanf("%d", &columns[num]) == 1 && columns[num] >= 0) {
        if (num > 0 && columns[num] >= 0 && columns[num]<columns[num-1]) {
            puts("invalid input. columns[num] must gather than columns[num-1]");
            exit(EXIT_FAILURE);
        }
        num ++;
    }

    printf("columns[%d] = %d\n",num,  columns[num]);

    if (num % 2 != 0) {
        puts("Last column number is not paired.");
        exit(EXIT_FAILURE);
    }

    while ((ch = getchar()) != EOF && ch != '\n')
        ;

    return num;
}

void rearrange(char *output, char const *input, int n_columns, int const columns[])
{
    int col;
    int output_col;
    int len;

    len = strlen(input);
    output_col = 0;

    for (col=0; col<n_columns; col+=2) {
        int nchars = columns[col+1] - columns[col] + 1;

        if (columns[col] >= len || output_col == MAX_INPUT - 1)
            break;

        if (output_col + nchars > MAX_INPUT - 1)
            nchars = MAX_INPUT - output_col - 1;

        strncpy(output + output_col, input + columns[col], nchars);
        output_col += nchars;
    }

    output[output_col] = '\0';
}

这个程序主要作用是循环从标准输入流中读取成对的标号,并以负数结尾,紧接着再循环读取标准输入流中的字符并按照之前成对的标号输出到屏幕上。

这里编译时,如果是按照书中的 gets(input) != NULL 那么会报

/tmp/cc7GVA4V.o: In function `main':
input.c:(.text+0x85): warning: the `gets' function is dangerous and should not be used.

因为 gets 函数对输入的字符没有长度限制,很有可能造成溢出,这里换成 fgets(input, MAX_INPUT, stdin) != NULL 就很好的规避了溢出问题。同时

while ((ch = getchar()) != EOF && ch != '\n') 
	;

主要是消除非法输入。因为 scanf 函数对输入值进行转换时,它只读取需要的字符,输入行包含的最后一个值的剩余部分仍会留在那里,等待被读取。比如输入 1 3 5 7 -1 然后换行,实际上 scanf 只读取 1 3 5 7 -1,并把换行留在 stdin 里,这时就需要清除掉。因为不清除掉,会出现以下情况

1 3 5 7 -1
Original input : 

Rearranged line: 

也就是换行符被下面的 fgets(input, MAX_INPUT, stdin) 接收并输出到屏幕了。这里也写成 fflush(stdin) 清空输入流。这个程序主要逻辑在 rearrange 方法里。

columns[0] = 1;
columns[1] = 3;
columns[2] = 5;
columns[3] = 7;
columns[4] = -1;

for (col=0; col<n_columns; col+=2) 成对循环成对复制 input 的字符串到 output

int nchars = columns[col+1] - columns[col] + 1;
/*
columns[0+1] - columns[0] + 1;
columns[1] - columns[0] + 1;
3 -1 + 1
3 
*/

并判断是否已到了是否超出了 input 数组,没有的话那么就使用库函数 strncpy 复制 inputnchars 长度到 output。

这个程序还有个问题,read_column_numbers里输入的下标除了最后一个应该是依次递增的,而不是乱序的

a < b < c < d

因此需要在 read_column_numbers函数 while 里添加判断

if (num > 0 && columns[num] >= 0 && columns[num]<columns[num-1]) {                                                                                                                     
    puts("invalid input. columns[num] must gather than columns[num-1]");
    exit(EXIT_FAILURE);
}

第 2 章 基本概念

主要讲述了环境、此法规则、程序风格。个人感觉就下面这个图重要,其他的就是理解并且编程时遵循其规则就行。
在这里插入图片描述
另外一个就是注意下三字母词序列
在这里插入图片描述
如何输出以下字符串

"Blunder??!??"
#include <stdio.h>

int main(void)
{
    puts("\"Blunder\?\?!\?\?\"");
    return 0;
}

第 3 章 数据

这章首先讲述了基本的数据类型,整型、浮点型、指针和聚合类型(如数组和结构);然后介绍了基本声明

int i,j;
int values[20];
char *msg="hello world";

这里需要关注的是,编译器并不检查程序对数组下标的引用是否越界,同时对于指针,需要理解的是

// 把 `msg` 声明为一个指向字符的指针,并用字符串常量中第 `1` 个字符的地址对该指针进行初始化
char *msg="hello world";

相当于

char *msg;
msg="hello world";

而对于函数,如果没有返回值,那么编译器默认返回 整型

f(int x)
{
    return x * x;
}

接着说了下 typedef,同时对比了下宏的区别,

#define ptr_to_char char * 
ptr_to_char a,b,c;

预处理器执行后,变成

char *a,b,c;

可以看到 bc 并没有如期的声明为字符型指针

typedef char *ptr_to_char;
ptr_to_char a,b,c;

上面的用法就说明了 typedef 意义,允许为各种数据类型定义新名字;再着就是常量了,使用 const 关键字来声明常量

// pi是一个普通的指向整型变量的指针
int *pi;

// pi是一个普通的指向整型变量的指针,可以修改指针的值,但不能修改它所指向的值
int const *pi;

// pi是一个普通的指向整型变量的指针,可以修改它所指向的值,但不能修改指针的值
int * const pi;

// pi是一个普通的指向整型变量的指针,无论是指针本身还是其指向的值都不能修改
int const * const pi;

这章的重点基本在最后几节,分别介绍了作用域、链接属性、存储类型和 static

  • 作用域:代码块作用域、文件作用域、原型作用域、函数作用域
  • 链接属性,这个可以参照上面 第 2 章 基本概念 编译过程的那个图
external(外部文件可见)、internal(此文件可见)、none(默认)
extern -> external、static -> internal,只对缺省的链接属性为 external 的声明有效
  • 存储类型:普通内存、运行时堆栈、硬件寄存器(register)

    • 静态变量内存:存储凡是在任何代码块之外声明的变量,在程序运行之前创建,在程序的整个执行中始终存在。
  • static 在不同的上下文中有不同的意义。

    • 用于函数定义时,或用于代码块之外时,用于改变标识符的链接属性,从 external 改为 internal
    • 用于代码块内部声明时,用于改变变量的存储类型,从自动变量修改为静态变量

一幅图说明四者的关系

在这里插入图片描述

第 4 章 语句

这章很轻松,和其他语言差不多,依次介绍了 空语句、表达式语句、代码块、if 语句、 while 语句、for 语句、 do 语句、switch 语句、goto 语句。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值