c语言杂记1

首先推荐的c语言的题:http://stevenkobes.com/ctest.html
 
只是简单的谈到了C语言的一些问题, 深入后后续更新。个人理解,可能理解不到位有偏差。如有错误,请指出。
 
该篇内容主要包括:
1.setjmp, longjmp 实现非本地跳转;
2.volatile关键字;
3.关于编译器优化;
4.左值lvalue和右值rvalue;
5.const char *p和char *const p;
6.-1 > 1?;
7.对于常量的修改会引发错误;
8.数据溢出和截断
 

 
    1.setjmp, longjmp 实现非本地跳转
     与goto不同,goto实现局部跳转。 jmp_buf变量来存放当前的环境,涉及到当前栈的信息,变量的存储等。首次调用setjmp会返回0。longjmp使用jmp_buf恢复到之前环境,程序又开始从setjmp执行起,并且返回值为longjmp设定的返回值。若在longjmp中设定的值为0,则setjmp会返回1。也可用于实现异常处理。
 
example:
#include <stdio.h>
#include <setjmp.h>

static jmp_buf buf;

int main(void)
{
   volatile int b = 3;

   if (setjmp(buf) != 0)
   {
      printf("%d\n", b);
      exit(0);
   }
   b = 5;
   longjmp(buf, 1);
}
解释:
首次调用setjmp会返回0,所以会执行b=5,调用longjmp,longjmp设置的返回值为0。然后程序从setjmp开始执行,返回1,进入printf,打印b值。
然后这里涉及到的一个volatile关键字。在下面会有说明。
 
2.volatile关键字
valatile关键字是告诉编译器该变量随时可变,用到时需每次从内存中取值,而不能放在寄存器中缓存,否则会造成数值不一样。
因为编译器会对代码进行优化。编译器进行优化的代码和不优化的代码变量的存取也是不同的。在3中会说到。
特别是在多线程中,多个线程都可能用到一个值,若不声明volatile,一个线程更改了该值,另一个线程的中该值没有更新过来。
还有就是在中断程序中,也可能改变某个值,但是编译器不知道。同样是取的寄存器中的旧值。
example:可以参照1上面的代码段。
若b不声明volatile,在优化和不优化的情况下输出结果是不同的。具体见下面3。
具体可参考:http://www.cnblogs.com/yc_sunniwell/archive/2010/06/24/1764231.html
 
3.关于编译器优化
编译器的优化可以是去掉无用的代码,比如在程序中从未使用过的变量;将变量存储到寄存器,加快存取速度等;调整指令顺序充分利用CPU指令流水线,常见的是重新排序读写指令。
具体看例子。
一个简单的例子。
example:
#include <stdio.h>
int main()
{
    int a = 3;
 
 
  
    return 0;
 
 
  
}
未进行优化的汇编代码如下:
图1
可以看到对a进行了赋值。
 
而进行优化过的代码汇编如下:
图2
可见,进行优化后,并未对a进行赋值,因为后续并没有使用a,不赋值也没事。
 
但是将a声明为volaitile之后,告诉编译器不要对其优化。汇编代码如图1。
 
有一段英文解释的很清楚,如下:
Note that the optimizations don't affect the global, static, and volatile variables; 
 
their values after the longjmp are the last values that they assumed. 
 
The setjmp(3) manual page on one system states that variables stored in memory will 
 
have values as of the time of the longjmp, whereas variables in the CPU and floating-point 
 
registers are restored to their values when setjmp was called. This is indeed what we see 
 
when we run the program in Figure 7.13. Without optimization, all five variables are stored in memory (the register hint is ignored for regival). When we enable optimization, both autoval and regival go into registers, even though the former wasn't declared register, and the volatile variable stays in memory. The thing to realize with this example is that you must use the volatile attribute if you're writing portable code that uses nonlocal jumps. Anything else can change from one system to the next.
大概意思是说,在未进行优化时,所有变量都在内存中。进行优化时,global,static,volatile不受影响,仍在内存。但是auto,register变量在寄存器中。在这里又扯到了setjmp,longjmp,在longjmp之前对值进行的修改,若是未优化的,值始终是修改过后的值;若是优化了的,则对与auto,register变量,当setjmp再次调用时,取的是还是之前保存在寄存器里的值。
下面这个例子可以证明上面这段话:
example:
#include <stdio.h>
#include <setjmp.h>
#include <stdlib.h>

static void f1(int, int , int, int, int *);
static void f2();

static jmp_buf jmpbuffer;
static int globval;

int main()
{
	int autoval = 2;
	register int regval = 3;
	static int statval = 5;
	volatile int volval = 4;
	int autoval2 = 0;

	globval = 1;

	if (setjmp(jmpbuffer) != 0)
	{
		printf ("after longjmp!\n");
		printf ("globval = %d, autoval = %d, regval = %d,"
			"statval = %d, volval = %d, autoval2 = %d\n",\
			globval, autoval, regval, statval, volval, autoval2);

		exit(0);
	}
	
        // 修改变量的值
 
 
  
	globval = 95;
	autoval = 96;
	regval = 97;
	volval = 98;
	statval = 99;

	f1(autoval, regval, volval, statval, &autoval2);
	exit(0);
}

static void f1(int i, int j, int k, int l, int *p)
{
	printf ("in f1():\n");
	printf ("globval = %d, autoval = %d, regval= %d, statval= %d,"
		"volval = %d, autoval2 = %d\n", globval, i, j, k, l, *p);

	*p = 11;
	f2();
}

static void f2()
{
	longjmp(jmpbuffer, 1);
}
在未进行优化时,结果为:
所有的值都被修改了。
 
在进行优化后,结果为:
auto,register变量仍是之前的值。
 
4.左值lvalue和右值rvalue
简单点来说,左值就是能够取到地址的变量或表达式。右值是取不到的。
example:
int a = 3;
a就是左值,3是右值。
再比如 int i = 0;
(++i) = 3;非法,在i+1后,然后取其一份拷贝,但不知道该值在内存中存于何处。
(i++) = 3;也非法。(i++)不是左值,是个右值。因为相当于拷贝i,然后原有i+1,此时仍然不知道拷贝i的地址。
具体参考:http://www.cnblogs.com/lua5/archive/2010/12/10/1896757.html
 
5.const char *p和char *const p
const char *p是指p指向的是(const char)类型的。char const *p与其一样。p的指向可以改变,但是p指向的内容不能改变。*p = ..不行,p = ..可行。
char *const p 是说p的指向是不能改变的,但是其指向的内容可以改变。p = ..不可行,*p = ..可行。
二级指针的const变体:
const char **p, char const **p 这两个是一样的,意思是p是一个指向(const char *)类型的指针;
char * const *p 是指p是一个指向带有const限定符的指针,该指针指向(char *)类型;
char **const p 是指p是一个带有const限定符的指针,指该指针向(char *)类型的指针;
 
const指针和非const指针之间的赋值:
有个规则摘自《C专家编程》:
两个操作数都是指向有限定符或无限定符的相容类型的指针,左边指针所指向的类型必须具有右边指针所指向类型的全部限定符。
个人理解:操作数所指类型要相容(有限定符或者无限定符)。左边指针所指类型限定符要包含右边的。
下面举例说明:
(1)一级指针:
char *p1, const char *p2, p2 = p1可行,因为p1指向char类型,p2指向const char类型,char和const char相容,p2所指类型有const限定符,p1所指类型无限定符,所以包含p1的指向的限定符,成立但是p1 = p2不可行。
(2)二级指针:
char **p1, const char **p2; p2 = p1不可行;p1是一个指向(char *)类型的指针,p2是一个指向(const char *)类型的指针,指向类型不相容
char **p1, char *const *p2; p2 = p1可行;p1是一个指向(char *)类型的指针, p2是一个指向带有const限定符的指针,该指针指向(char *)类型。由上可知,相容。
const char * const *p2; p是一个指向带有const限定符的指针,该指针指向(const char *)类型
const char **p1;
p2 = p1; 可行,指向类型相同,并且包含p1的全部限定符,因为它指向类型有const,p2指向的无限定符。
char *const *p1;
p2 = p1;不可行,指向类型不相容。
 
具体参考:http://www.cnblogs.com/rushuizhijing/archive/2011/08/26/2154737.html
 
6.-1 > 1?
有可能成立,这个涉及到数据类型之间的转换。
若int a = -1;
unsigned b = 1;
if (a < b)
{
    // 不会执行
 
 
  
    ...
}
结果不会预期。
原因是int和unsigned int进行运算时,有符号数会转换成无符号数,这里假设sizeof(int) = 4,则-1被转换成0x7fffffff,巨大的数。
 
7.对于常量的修改会引发错误
example:
char *p = "124";
*p = '2';
会出现错误。因为p指向一个常量区域,不允许修改里面的值。
 
8.数据溢出和截断
example:
溢出:
unsigned int a = 0xffffffff;
a + 1 的值为?
a + 1会溢出,0x100000000, 取低32位。结果为0;
 
截断:
short a = 0xff00;
char b = a;
b值为?
short为2字节,char为1字节,赋值时会取a的低8为,b = 0;
 
上面的有的写的很简单,只是提到会出现这样的问题,没有具体的分析。
下次再继续写。字节对齐,指针等东西。

转载于:https://www.cnblogs.com/silan-liu/archive/2013/03/11/2954580.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值