C语言—扫盲补充
1. 内存管理:
void *calloc(int num , int size)——在内存中动态的分配num个长度为 size的连续空间,并初始化为0
void *malloc(int num)——在堆区分配一块指定大小的内存空间,内存空间未初始化,值是未知的
void *realloc(void *address,int newsize)——重新分配内存,并把内存扩展到newsize
void free(void * address)—— 释放address指向的内存块,释放动态分配的内存
2. C的作用域规则
—局部变量:在函数或块内部——栈中,只在调用时分配内存
—全局变量:在所有函数外边——静态存储
—形式参数:在函数的参数定义中
当局部变量被定义时,系统不会对其初始化,您必须自行对其初始化。
3. 枚举
enum 枚举名 {元素1,元素2......};
enum DAY{ MON=1, TUE, WED, THU, FRI, SAT, SUN}; // 每个元素都可以赋值,后面的元素值,如果没有赋值,则为前面元素值加一
枚举看起来比宏定义更简洁
#define MON 1
#define TUE 2
...
#define SUN 7
注意:第一个枚举成员的默认值为整型的 0,后续枚举成员的值在前一个成员上加 1。我们在这个实例中把第一个枚举成员的值定义为 1,第二个就为 2,以此类推。
4. 函数指针
函数指针是指向函数的指针变量,
typedef int (*fun_ptr)(int,int); // 声明一个指向同样参数、返回值的函数指针类型
#include <stdio.h>
int max(int x, int y)
{ return x > y ? x : y;}
int main(void){
/* p 是函数指针 */
int (* p)(int, int) = & max; // &可以省略 int a, b, c, d;
printf("请输入三个数字:");
scanf("%d %d %d", & a, & b, & c);
/* 与直接调用函数等价,d = max(max(a, b), c) */
d = p(p(a, b), c);
printf("最大的数字是: %d\n", d);
return 0;
}
回调函数:函数指针作为某个函数的参数。
函数指针变量可以作为某个函数的参数来使用的,回调函数就是一个通过函数指针调用的函数。
简单讲:回调函数是由别人的函数执行时调用你实现的函数。
5. 关于size_t
size_t 是一种数据类型,近似于无符号整型,但容量范围一般大于 int 和 unsigned。
但凡不涉及负值范围的表示size取值的,都可以用size_t;比如array[size_t]。
size_t 在stddef.h头文件中定义。
在其他常见的宏定义以及函数中常用到有:
1,sizeof运算符返回的结果是size_t类型;
2,void *malloc(size_t size)...
类似的还有wchar_t, ptrdiff_t。
wchar_t就是wide char type,“一种用来记录一个宽字符的数据类型”。
ptrdiff_t就是pointer difference type,“一种用来记录两个指针之间的距离的数据类型”。
通常,size_t和ptrdiff_t都是用typedef来实现的。你可能在某个头文件里面找到类似的语句:
typedef unsigned int size_t;
6. 错误处理
C 语言不提供对错误处理的直接支持,它以返回值的形式允许您访问底层数据。在发生错误时,大多数的 C 或 UNIX 函数调用返回 1 或 NULL,同时会设置一个错误代码 errno,该错误代码是全局变量,表示在函数调用期间发生了错误。您可以在 errno.h 头文件中找到各种各样的错误代码。
所以,C 程序员可以通过检查返回值,然后根据返回值决定采取哪种适当的动作。开发人员应该在程序初始化时,把 errno 设置为 0,这是一种良好的编程习惯。0 值表示程序中没有错误。
c语言提供了perror()和strerror()函数来显示与errno相关的文本消息。
perror() 函数显示您传给它的字符串,后跟一个冒号、一个空格和当前 errno 值的文本表示形式。
strerror() 函数,返回一个指针,指针指向当前 errno 值的文本表示形式。
perror()用于实时显示,strerror用于存储,便于后续调试。
7. 递归问题
C 语言支持递归,即一个函数可以调用其自身。但在使用递归时,程序员需要注意定义一个从函数退出的条件,否则会进入死循环。
递归函数在解决许多数学问题上起了至关重要的作用,比如计算一个数的阶乘、生成斐波那契数列,等等。
递归是一个简洁的概念,同时也是一种很有用的手段。但是,使用递归是要付出代价的。与直接的语句(如while循环)相比,递归函数会耗费更多的运行时间,并且要占用大量的栈空间。递归函数每次调用自身时,都需要把它的状态存到栈中,以便在它调用完自身后,程序可以返回到它原来的状态。未经精心设计的递归函数总是会带来麻烦。
采用递归方法来解决问题,必须符合以下三个条件:
1、可以把要解决的问题转化为一个新问题,而这个新的问题的解决方法仍与原来的解决方法相同,只是所处理的对象有规律地递增或递减。说明:解决问题的方法相同,调用函数的参数每次不同(有规律的递增或递减),如果没有规律也就不能适用递归调用。
2、可以应用这个转化过程使问题得到解决。说明:使用其他的办法比较麻烦或很难解决,而使用递归的方法可以很好地解决问题。
3、必定要有一个明确的结束递归的条件。说明:一定要能够在适当的地方结束递归调用。不然可能导致系统崩溃。
8. 命令行参数
执行程序时,可以从命令行传值给 C 程序。这些值被称为命令行参数,它们对程序很重要,特别是当您想从外部控制程序,而不是在代码内对这些值进行硬编码时,就显得尤为重要了。
命令行参数是使用 main() 函数参数来处理的,其中,
argc 是指传入参数的个数
argv[] 是一个指针数组,指向传递给程序的每个参数
应当指出的是,argv[0] 存储程序的名称,argv[1] 是一个指向第一个命令行参数的指针,*argv[n] 是最后一个参数。如果没有提供任何参数,argc 将为 1,否则,如果传递了一个参数,argc 将被设置为 2。
多个命令行参数之间用空格分隔,但是如果参数本身带有空格,那么传递参数的时候应把参数放置在双引号 "" 或单引号 '' 内部。让我们重新编写上面的实例,有一个空间,那么你可以通过这样的观点,把它们放在双引号或单引号""""。让我们重新编写上面的实例,向程序传递一个放置在双引号内部的命令行参数:
$./a.out "testing1 testing2"
9. assert 用法
编写代码时,我们总是会做出一些假设,assert就是用于在代码中捕捉这些假设。
C 标准库的 assert.h头文件提供了一个名为 assert 的宏,它可用于验证程序做出的假设,并在假设为假时输出诊断消息。
#define assert(ignore) ((void)0)
10. signal.h
【Linux函数】Signal ()函数详细介绍 - 华秋实的专栏 - CSDN博客
signal.h是C标准函数库中的信号处理部分, 定义了程序执行时如何处理不同的信号。信号用作进程间通信, 报告异常行为(如除零)、用户的一些按键组合(如同时按下Ctrl与C键,产生信号SIGINT)。
signal:
void (*signal(int sig, void (*handler)(int)))(int);
SIG_ 宏与 signal 函数一起使用来定义信号的功能
1SIG_DFL 默认的信号处理程序。
2SIG_ERR 表示一个信号错误。
3SIG_IGN 忽视信号
signal()用于确定以后当信号sig出现时的处理方法。如果handler的值是SIG_DFL,那么就采用实现定义的缺省行为;如果handler的值是SIG_IGN,那么就忽略该信号;否则,调用handler所指向的函数(参数为信号类型)。有效的信号包括:
SIGABRT异常终止,如调用abort()。
SIGFPE算术运算出错,如除数为0或溢出。
SIGILL非法函数映象,如非法指令。
SIGINT交互式信号,如中断。
SIGSEGV非法访问存储器,如访问不存在的内存单元。
SIGTERM发送给本程序的终止请求信号。
raise:
#include <signal.h>
int raise(int sig);
向程序发送信号sig。如果发送不成功,就返回一个非0值。
11. 输入输出函数
int getchar(void): 从屏幕读取下一个可用的字符,并把它返回为一个整数。
int putchar (void ) : 函数把字符输出到屏幕上,并返回相同的字符。
int c;
printf( "Enter a value :");
c = getchar( );
printf( "\nYou entered: ");
putchar( c );
printf( "\n");
char *gets(char *s) : 函数从stdin读取亿航到s所指向的缓冲区,直到一个终止符或EOF
char *fgets(char *s, int n, FILE *stream);//可以这么使用fgets(str, sizeof(str), stdin)
——fgets函数功能:从文件指针stream中读取字符,存到以s为起始地址的空间里,直到读完N-1个字符,或者读完一行。
int puts(const char*s) : 函数把字符串s和一个尾随的换行符写入到stdout
注意:gets函数由于没有指定输入字符大小,所以会无限读取,一旦输入的字符大于数组长度,就会发生内存越界,从而造成程序崩溃或其他数据的错误。另外linux下不支持gets 和 puts,需要使用fgets 和fputs
char str[100];
printf( "Enter a value :");
gets( str );
printf( "\nYou entered: ");
puts( str );
int scanf(const char *format,...)//函数从标准输入流stdin读取输入,并根据提供的format来浏览输入
int printf(const char *format,...)// 函数把输出写入到标准输出流 stdout,并根据提供的格式产生输出。
char str[100];
int i;
printf( "Enter a value :");
scanf("%s %d", str, &i);
printf( "\nYou entered: %s %d ", str, i);
printf("\n");
常见错误问题:
学 C 语言的时候,字符输入曾经困扰过我,例如这段代码:
int i; char c;
scanf("%d%c", &i,&c);
这时候变量 c 中存储的往往不是你想输入的字符,而是一个空格,然后我们又会这样来写:
int i;char c;
scanf("%d", &i);scanf("%c", &c);
这时候,我们发现,根本没有输入字符C的机会,这是为什么?因为输入流是有缓冲区的,我们输入的字符存储在那,然后再赋值给我们的变量。我们可以这样改:
int i;char c;scanf("%d", &i);while((c=getchar())==' ' || c=='\n');c = getchar();
这个办法是一直读取,读到没有空格和换行就跳出循环,但是有一个更好的解决办法;
int i;char c;
scanf("%d%[^' '^'\n']", &i, &c);
这是用正则表达来控制输入格式为非空格非换行。
注意:在使用标准输入函数,需要通过stdin输入时候,请注意 空格和 回车换行的问题。此时正则表达式是一个很好的解决问题的方式。