C和指针 1.1 简介

缘起

《C和指针》1.1节

分析

1
2
3
4
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_COLS 20

这称为预处理指令(preprocessor directives),这些指令是预处理器(preprocessor)解释的. 预处理器读入源码,根据预处理器指令进行修改,然后把修改过的源代码递交给编译器. 例如预处理器用名为stdio.h的库函数头文件的内容替换了第一条的 #include <stdio.h>指令语句. 其结果就仿佛是stdio.h的内容被逐字逐句写到了源文件的那个位置.

stdlib.h中引入了

1
2
3
4
/* Definition of the argument values for the exit() function */

#define EXIT_SUCCESS 0
#define EXIT_FAILURE 1

关于头文件有如下最佳实践

1
如果有一些声明需要用于几个不同源文件,则完全可以在一个单独的文件中编写这些声明,然后使用#include指令把这个文件包含到需要使用这些声明的源文件中。这样就只需要一份拷贝,用于许多不同的地方.

函数原型告诉编译器这些以后将在源代码中定义的函数的特征. 这样的话,当这些函数被调用的时候,编译器就能对它们进行准确性的检查. 函数原型由函数返回值类型、函数名称、入参类型构成. 这个在java中称为函数原型.

gets函数(头文件是 stdio.h)从stdin读取一行文本并且将它存储到作为参数传入的数组中,一行的输入以一串字符构成 换行符结尾,gets将丢弃换行符,并且在该串字符末尾存储一个\0(NULL, 在stdio.h中定义),然后gets函数返回一个非零的值表示该行已经成功读取. 否则返回NULL表示已经到达了输入的末尾(文件尾). 也就是gets函数只有在遇到换行符或者文件尾的时候才会结束输入,由于gets()不检查字符串字符串的大小,因此容易造成缓存溢出的安全性问题导致程序崩溃.

举个例子

1
2
3
4
5
6
7
8
#include <stdio.h>
int main(void)
{
char str[10];
printf("Input a string.\n");
gets(str);
printf("The string you input is: %s",str); //输出所有的值,注意a
}

如果输入123456(长度小于10),则输出结果为:
Input a string.
123456↙
The string you input is:123456

如果输入12345678901234567890(长度大于10),则输出结果为:
Input a string.
12345678901234567890↙
The string you input is:12345678901234567890
同时看到系统提示程序已经崩溃。 如果不能正确使用gets()函数,带来的危害是很大的,就如上面我们看到的,输入字符串的长度大于缓冲区长度时,并没有截断,原样输出了读入的字符串,造成程序崩溃。 考虑到程序安全性和健壮性,建议用fgets()来代替gets()。

如果你在GCC中使用gets(),编译无法通过,会提示:
the ‘gets’ function is dangerous and shout not be used.

C语言中并不存在string类型的数据,但是C语言约定——字符串就是一串以\0结尾的字符. \0称为字符串的休止符,但是它并不作为字符串的一部分. 字符串常量(string literal)就是 “hello”这种,它在内存中占据6个字节——包括’\0’.

scanf从stdin中读取字符并根据格式字符串对他们进行转换. scanf函数的返回值是函数成功转换并存储于参数中的值的个数. scanf除了 %c 之外,输入值之前的空白(空格、tab、换行符)都将被跳过

C标准并未硬性规定C编译器对数组下标的合法性做校验. 而且事实上绝大多数C编译器确实也不做此项检查.

puts函数是gets函数的输出版本,它把指定的字符串写到标准输出并在末尾添加一个换行符.

scanf 会保留换行符在缓冲区,而gets不会保留换行符在缓冲区. 所以使用scanf或者gets之后,再使用getchar得到的字符是不一样的, 前者会得到换行符, 后者得到的是真正的下一个字符.

EOF 在stdio.h中定义. 它是-1 是一个整型. 这就解释了为什么getchar() 返回的是整型. 因为要和EOF比较来判断是否到达文件尾部.

const修饰符导致编译器会去检查是否被修改,一旦代码中有修改的话,编译就会报错而无法通过编译.

strncpy(dst, src, n); 表示把src的前n个字符(不够的话,则以\0填充)

strcpy(dst, src) 和 strncpy较为相似——不限制复制的字节数量.

strcat(dts, src) 将src接到dst的后面. 并且最后会加一个 \0

注意, strcpy和strcat都不对字符串是否有足够空间做验证(这是程序员自己的责任)。C标准没有要求它们这么做. 而且, 这两个函数都要求第一个参数不是const的.

字符串内搜索的函数是 strchr(char *s, char a), 就是在s中搜索a,返回a在s中第一次出现的位置的指针. 如果没找到,返回NULL指针

strstr(char *s, char *t)是在字符串s中搜索字符串t,返回t在s中出现的第一次出现的位置指针, 没搜到返回NULL指针.