ANSI C (6) —— 指针、断言、信号、跳转

网友投稿 580 2022-08-27

ANSI C (6) —— 指针、断言、信号、跳转

ANSI C (6) —— 指针、断言、信号、跳转

指针

下面的三条语句是等价的,但是我们常常使用的是第一种形式int *p,同时我们注意,他们是在初始化p而不是*p。

#include #include int main(){ int a=12; int *p1=&a; int* p2=&a; int * p3=&a; printf("p1: %p %d\n",p1,*p1); printf("p2: %p %d\n",p2,*p2); printf("p3: %p %d\n",p3,*p3); return 0;}/*p1: 0028FF10 12p2: 0028FF10 12p3: 0028FF10 12*/

指向void 类型的指针可以指向其他的任何的数据类型变量的地址。任何类型的指针也可以指向void.

int main(){ int a=12; double b=12.99; void *p=&a; printf("%d\n",*((int *)p)); p=&b; printf("%lf\n",*((double *)p)); return 0;}/*1212.990000*/

我们可以在变量value前使用&但是不能在value ‘计算符’ ‘常量或变量’前加&,因为value ‘计算符’ ‘常量或变量’得到的是常量

int a=12,b=10; int *p=&(a+b); // error: lvalue required as unary '&'

数组和指针

数组名是一个指针常量,数组定义时设置成数组第一个单元的地址,从此以后就不能修改,​​int a[3]; a=...​​​是错误的。 但是修改数组的单元是合法的,​​​a[1]=...​​​。 指针变量可以进行赋值操作。

#include int main(){ char *p=NULL; char s[10]="123456789"; p=s; printf("%s\n",p); p=s+2; /* 等价于 p=&s[2]; [2]表示和数组首地址的偏移量是2 */ printf("%s\n",p);   p=s+4; /* 等价于p=&s[4]; [4]表示和数组首地址的偏移量是4 */   printf("%s\n",p);   return 0;}/*123456789345678956789*/

利用数组首地址输出字符串:

#include #include int main(){    char s[5]="1234";    printf("%s\n",s);    printf("%s\n",&s[0]);    return 0;}/*12341234*/

多级间接访问

指向指针的指针,比如 ​​int ** p​​​ (指向int型变量的指针变量的指针变量) 一次类推还有,​​​int *** p​​​ (p是类型为​​int***​​​的变量,三个星号,代表着三次间接访问) 如下列代码:

int a = 12; int* p1 = &a; int** p2 = &p1; int*** p3 = &p2; printf("%d\n",***p3); ***p3 = 14; printf("%d\n",a);

常量指针与指针常量

const修饰的是数据类型,则为常量指针。(const char* p) const修饰的是指针本身,那么则为指针常量。(char* const p)

#include #include char s[] = "I love you";void try1(){ const char* p = s; /* const decorate "char*" */ //*p='H'; /* error: assignment of read-only location ‘*p’ */}void try2(){ char* const p = s; /* const decorate "p" */ *p='H'; /* p can't point to another addr */}int main(){ char* p = s; *p='H'; try1(); try2(); printf("%s\n",s); return 0;}/*output:H love you*/

指向函数的指针

int (*p)(char ) /* p是一个指向参数为char,返回值是int的函数的指针 */int *p(char ) /* p是函数名,该函数的参数为char,返回值类型是int * */

调用相关的方式: ​​​(*p)(ch)​​​ 或者 ​​​p(ch)​​​ 比如:

int get(char ch){ return ch;}int main(){ int (*p)(char); p = &get; printf("%d\n",p('A')); printf("%d\n",(*p)('A')); return 0;}/*6565*/

函数指针应用: 在程序中fork一个子进程,在子进程中弹出选择框,询问用户喜欢哪一类操作系统,然后回到父进程正常退出。

#include #include void fun1(){ puts("windows OS ");}void fun2(){ puts("linux OS ");}void fun3(){ puts("Mac OS ");}int main(){ int ret = fork(); if(ret == -1){ perror("fork "); return -1; } else if(ret == 0){flag: printf("please tell me which OS do you like most:\n1.windows\n2.linux/Unix\n3.Mac OS\nenter a number: "); int ret; scanf("%d",&ret); void (*func[3])(); /* function pointer array. */ func[0] = &fun1; func[1] = &fun2; func[2] = &fun3; if(ret>=1 && ret<=3){ (*func[ret-1])(); } else goto flag; } else { int status; wait(&status); puts("end...."); } return 0;}

执行:

$ ./func please tell me which OS do you like most:1.windows2.linux/Unix3.Mac OSenter a number: 0please tell me which OS do you like most:1.windows2.linux/Unix3.Mac OSenter a number: 2linux OS end....

restrict指针的疑惑

restrict是c99标准引入的,用于限定和约束指针,它告诉编译器,所有修改该指针所指向内存中内容的操作都必须通过该指针来修改, 任何同样指向这个内存单元的其他指针都是无效指针。 但是下面的例子不是在说“无效指针是有效的”吗?

#include #include int main(){ int a = 12; int *restrict p = &a; int * p2 = &a; *p = 23; printf("%d %d\n",*p,*p2); *p2 = 24; printf("%d %d\n",*p,*p2); return 0;}

执行:

[edemon@CentOS workspace]$ gcc -std=c99 restrict.c [edemon@CentOS workspace]$ ./a.out 23 2324 24[edemon@CentOS workspace]$ gcc -std=gnu99 restrict.c [edemon@CentOS workspace]$ ./a.out 23 2324 24

断言

断言assertion是一个程序特定执行点上必须满足的条件,如果断言不满足,系统将打印错误。 例如:

#include #include #include int main(){ int i; for(i=0;i<10;i++){ assert(i<9); } return 0;}/*Assertion failed: i<9, file E:\code\assert\main.c, line 9This application has requested the Runtime to terminate it in an unusual way.Please contact the application's support team for

使用断言需要一定的系统开销,可以在assert.h前增加宏NDEBUG来使得断言设定失效。

#define NDEBUG#include

信号

程序执行的过程中如果出现错误,计算机系统将产生一个信号来表示发生了异常,C可以调用自己的函数来处理这些信号。 也即是异常处理。 相关的函数是signal() ​​​sighandler_t signal(int signum, sighandler_t handler);​​​ signal()返回上一个信号处理函数的handler的返回值。 三种方式处理异常信号:

function

effect

SIG_DFL

终止程序

SIG_IGN

忽略

自定义函数

自定义处理动作

比如程序:

#include #include #include int main(){ signal(SIGINT,SIG_IGN); while(1); return 0;}

即使按下ctrl + c,系统也不会终止程序。 我们也能自定义信号处理函数,比如​​​javascript:void(0)​​ 的《11.自定义中断信号SIGINT的处理函数》。

跳转

这里说的跳转不是goto,而是setjmp(), longjmp().前者设置了跳转的终点,longjmp()则是跳转的入口。

void longjmp(jmp_buf env, int val);If longjmp() is invoked with a second argument of 0, 1 will be returned instead.void int

例子:

#include #include #include jmp_buf env;void jmper(){ longjmp(env,0); puts("hello");}void get(){ setjmp(env); puts("end"); return ; }int main(){ get(); jmper(); return 0;}

执行:

[edemon@CentOS workspace]$ gcc jmp.c [edemon@CentOS workspace]$ ./a.out endend

goto语句是无法实现不同函数间的跳转的。如上的例子,我们修改成:

#include #include void get(){tag: puts("end"); return ; }void jmper(){ goto tag; puts("hello");}int main(){ get(); jmper(); return 0;}

不出现意外的话,编译的时候会有类似这样的信息:

error: label ‘tag’ used but not defined goto

其他

块和局部变量

C可以使用块{}来影响变量的作用范围。

#include #include int main(){ int a=10; printf("%d\n",a); { int a=20; printf("%d\n",a); } return 0;}/*output:1020*/

关于char的负数问题

关于char的负数问题: ASCII码只有0~127,但是中文是用多字节来表示的,随着字符编码的不同,每一个汉字所占的字节数也是不同的,例如GBK一个汉字两个字节,utf-8一个汉字三个字节。 比如:

/*Linux CentOS.com 2.6.32-642.6.2.el6.i686 #1 SMP Wed Oct 26 06:14:53 UTC 2016 i686 i686 i386 GNU/Linux*/#include #include #include int main(){ char *str = (char *)malloc(4); strcpy(str,"好"); printf("%d %d %d %d\n",str[0],str[1],str[2],str[3]); free(str); return 0;}/*-27 -91 -67 0*/

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:qt (1) —— 入门
下一篇:汇编 —— 起步
相关文章

 发表评论

暂时没有评论,来抢沙发吧~