? 在C語言編程中,有許多高級技巧可以幫助我們更好地解決問題,提高代碼的效率和可維護(hù)性舞竿。在本文中, 本人根據(jù)工作多年的開發(fā)經(jīng)驗(yàn)總結(jié)出平時(shí)容易出錯(cuò)的知識點(diǎn)和一些常用的技巧方法, 使初學(xué)者少走彎路,避開之前走過的坑路,來提高開發(fā)的效率,總結(jié)出問題排查的一些方法掏秩。
1. 關(guān)于變量占位地址大小的問題:
---------------------------例子1-----------------------
(1)以下為Linux下的32位C程序,請計(jì)算sizeof的值荆姆。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void Func (char str2[100], int size)
{
printf (" sizeof (str2):%d.\n", sizeof(str2));
}
int main (int argc, char **argv)
{
char str1[] = "hello";
char *p = str1;
int? n = 10;
void *p2 = malloc(100);
char str2[100] = {0};
printf ("\n sizeof(str)=%d. \n", sizeof(str1));
printf (" sizeof(p)=%d.\n", sizeof(p));
printf (" sizeof(n)=%d.\n", sizeof(n));
printf (" sizeof(p2)=%d.\n", sizeof(p2));
Func (str2, 100);
return 0;
}
32位機(jī)器輸出結(jié)果如下:
1) sizeof(str1)= 6 ##字符串5個(gè)字符+一個(gè)結(jié)束符'\0' = 6個(gè)字節(jié)蒙幻;
2) sizeof(p)= 4 ##指針變量,大小固定為4個(gè)字節(jié)胞枕;
3) sizeof(n)= 4 ##int類型杆煞, 大小固定為4個(gè)字節(jié);
4) sizeof(p2)= 4 ##p2也是指針類型,大小為4個(gè)字節(jié)决乎,指向分配的100個(gè)字節(jié)首地址队询;
5) sizeof(str2)= 4 ##str2為形參,也是指針類型构诚,大小固定為4個(gè)字節(jié)蚌斩;
64位機(jī)器輸出結(jié)果如下:
1) sizeof(str1)= 6 ##字符串5個(gè)字符+一個(gè)結(jié)束符'\0' = 6個(gè)字節(jié);
2) sizeof(p)= 8 ##指針變量范嘱,大小固定為8個(gè)字節(jié)送膳;
3) sizeof(n)= 4 ##int類型, 大小固定為4個(gè)字節(jié)丑蛤;
4) sizeof(str2)= 8 ##p2也是指針類型叠聋,大小為4個(gè)字節(jié)
5) sizeof(p2)= 8 ##p2也是指針類型,大小為4個(gè)字節(jié)
---------------------------例子2-----------------------
(2).Linux下的64位C程序打印值是多少受裹?
typedef union
{
long i;
int k[5];
char c;
}Item1;
typedef struct __data{
int? ? cat;
Item1? cow;
double dog;
} Item2;
DATE max;
? ? printf("sizeof item1: %d, sizeof item2: %d.\n", sizeof(Item1), sizeof(Item2));
執(zhí)行結(jié)果:sizeof item1: 24, sizeof item2: 40.
解析:
32位系統(tǒng)一般為4字節(jié)對齊,64位系統(tǒng)一般為8字對齊碌补,所以在64位ubuntu系統(tǒng)下結(jié)果為:
--Item1是union類型,所有成員變量共用地址空間棉饶,要看最大變量所占地址厦章,由于8字節(jié)對齊,
k[5]占20個(gè)字節(jié)照藻,8字節(jié)對齊袜啃,所以為24個(gè)字節(jié);
--Item2是struct 類型幸缕,所有成員地址之和群发,并考慮字節(jié)對齊,所以結(jié)構(gòu)體大小為:
4+4(int8字節(jié)對齊) + 24(union類型) + 8(double類型為8個(gè)字節(jié)) = 40
2. 字節(jié)對齊問題:
1)結(jié)構(gòu)體對齊規(guī)則
<1> 每個(gè)數(shù)據(jù)成員的起始位置必須是自身大小的整數(shù)倍冀值;
<2> 結(jié)構(gòu)體總大小必須是結(jié)構(gòu)體成員中最大的對齊模數(shù)的整數(shù)倍也物;
<3> 結(jié)構(gòu)體包含數(shù)組時(shí)宫屠,按單個(gè)類型對齊方式列疗;
<4> 共用體union取成員的最大內(nèi)存,但包含在結(jié)構(gòu)體內(nèi)時(shí)浪蹂,按union內(nèi)部最大類型字節(jié)數(shù)的整數(shù)倍開始存儲抵栈; 以下均在32位系統(tǒng)默認(rèn)4字節(jié)對齊的情況下測試:
-----------------------------------例子1-----------------------------------
struct item_data1
{
? char? a;
? int? b;
? short c;
};
結(jié)構(gòu)體大小(32位系統(tǒng)):12個(gè)字節(jié)
解析:char占一個(gè)字節(jié)坤次,int占四個(gè)字節(jié)古劲,由于int的起始地址要在4的倍數(shù)上,char后邊補(bǔ)齊3個(gè)字節(jié)缰猴,
shor占兩個(gè)字節(jié)产艾,但是整個(gè)結(jié)構(gòu)體大小要是最大的對齊模數(shù)的整數(shù)倍,即4的倍數(shù),所以補(bǔ)兩個(gè)字節(jié)闷堡,一共12個(gè)字節(jié)隘膘。
-----------------------------------例子2-----------------------------------
struct item_data2
{
char? a;
short c;
int? b;
};
結(jié)構(gòu)體大小(32位系統(tǒng)):8個(gè)字節(jié)杠览;
解析:char占一個(gè)字節(jié)弯菊,short占兩個(gè)字節(jié),由于short的起始地址要在2的倍數(shù)上踱阿,char后邊補(bǔ)齊1個(gè)字節(jié)管钳;
int占四個(gè)字節(jié),剛好在4的倍數(shù)上软舌,所以總共8個(gè)字節(jié)才漆;
-----------------------------------例子3:結(jié)構(gòu)體嵌套結(jié)構(gòu)體-----------------------------------
結(jié)構(gòu)體包含另一個(gè)結(jié)構(gòu)體成員,則被包含的結(jié)構(gòu)體成員要從其原始結(jié)構(gòu)體內(nèi)部的最大對齊模數(shù)的整數(shù)倍地址開始存儲(比如struct a里含有struct b佛点,b中有char栽烂、double 、int 元素恋脚,那么b應(yīng)該從8(double)的整數(shù)倍開始存儲)
struct item_data3
{
char? a;
struct item_data1 b[2];
short c;
};
結(jié)構(gòu)體大邢侔臁(32位系統(tǒng)):32個(gè)字節(jié);
解析: char占一個(gè)字節(jié)糟描,char后邊補(bǔ)齊3個(gè)字節(jié)怀喉;結(jié)構(gòu)體item_data1占12個(gè)字節(jié),2個(gè)成員占24個(gè)字節(jié)船响;
成員c 占2個(gè)字節(jié)躬拢,后邊補(bǔ)齊2個(gè)字節(jié),所以總大小為:4+12*2+4 = 32见间;
-----------------------------------例子4:結(jié)構(gòu)體嵌套u(yù)nion-----------------------------------
結(jié)構(gòu)體包含共用體成員聊闯,則該共用體成員要從其原始共用體內(nèi)部成員中的最大對齊模數(shù)的整數(shù)倍地址開始存儲
struct item_data4
{
char? a;
union x{
char m;
int? n;
}? b;
short c;
};
結(jié)構(gòu)體大小(32位系統(tǒng)):12個(gè)字節(jié)米诉;
解析:?char占一個(gè)字節(jié)菱蔬,char后邊補(bǔ)齊3個(gè)字節(jié);共用體最大為4個(gè)字節(jié)史侣,成員c為short類型拴泌,需要補(bǔ)齊2個(gè)字節(jié); 所以總大小為:4+4+4 = 12惊橱;
-----------------------------------例子5:結(jié)構(gòu)體包含0數(shù)組-----------------------------------
struct byte2
{
? char a;
? short c;
? int b;
? double d[0];
};
結(jié)構(gòu)體大序礁(32位系統(tǒng)):8個(gè)字節(jié);
解析:
長度為0的數(shù)組的作用是為了滿足需要可變長度的結(jié)構(gòu)體税朴,具體用法是在一個(gè)結(jié)構(gòu)體的最后回季,申明一個(gè)長度為0的數(shù)組家制,就可以使得這個(gè)結(jié)構(gòu)體是可變長的。
對于編譯器來說泡一,此時(shí)長度為0的數(shù)組并不占用空間慰丛,因?yàn)閿?shù)組名本身不占空間,它只是一個(gè)偏移量瘾杭,數(shù)組名這個(gè)符號本身代表了一個(gè)不可修改的地址常量诅病。
2) 共同體union對齊規(guī)則
共同體的內(nèi)存除了取最大成員內(nèi)存外,還要保證字節(jié)對齊粥烁。
union byte3
{
char? a[6];
short c[5];
int? b;
};
共同體byte3中最大成員就是short c[5]贤笆,占10個(gè)字節(jié),由于還要保證字節(jié)對齊讨阻,所以是10+2=12芥永;
3) 位域字節(jié)對齊規(guī)則
a. “位域”是把一個(gè)字節(jié)中的二進(jìn)位劃分為幾個(gè)不同的區(qū)域,并說明每個(gè)區(qū)域的位數(shù)钝吮。使用位域的主要目的是壓縮存儲埋涧。 位域列表的形式為: 類型說明符 位域名:位域長度(單位:位 bite)
如:struct bs
? ? ? {
? ? ? ? ? int a:8;
? ? ? ? ? int b:2;
? ? ? ? ? int c:6;
? ? ? } data;
其中位域a占8位,位域b占2位奇瘦,位域c占6位棘催。
b. 位域說明:
<1>. 一個(gè)位域必須存儲在同一個(gè)字節(jié)中,不能跨兩個(gè)字節(jié)耳标。如一個(gè)字節(jié)所蚀及樱空間不夠存放另一位域時(shí),應(yīng)從下一單元起存放該位域次坡。
<2>. 由于位域不允許跨兩個(gè)字節(jié)呼猪,因此位域的長度不能大于一個(gè)字節(jié)(8位)的長度,也就是說不能超過8位二進(jìn)位砸琅。
<3>. 位域可以無位域名宋距,這時(shí)它只用來作填充或調(diào)整位置。無名的位域是不能使用的症脂。例如: int :2
c.位域?qū)R規(guī)則:
1) 如果相鄰位域字段的類型相同谚赎,且其位寬之和小于類型大小,則后面的字段將緊鄰前一個(gè)字段存儲摊腋,直到不能容納為止沸版;
2) 如果相鄰位域字段的類型相同,但其位寬之和大于類型的sizeof大小兴蒸,則后面的字段將從新的存儲單元開始,其偏移量為其類型大小的整數(shù)倍细办;
3) 如果相鄰的位域字段的類型不同橙凳,從新的存儲單元開始蕾殴,偏移量為其類型大小的整數(shù)倍,即不壓縮岛啸;
4) 如果位域字段之間穿插著非位域字段钓觉,則不進(jìn)行壓縮;
5) 整個(gè)結(jié)構(gòu)體的總大小為最寬基本類型成員大小的整數(shù)倍坚踩。
-----------------------------------例子1----------------------------------
例題:
(1)typedef struct? AA
{
? unsigned int b1:5;
? unsigned int b2:5;
? unsigned int b3:5;
? unsigned int b4:5;
? unsigned int b5:5;
}AA;
sizeof(AA)= 4
【解析】參考規(guī)則 1)荡灾。
由于相鄰成員類型相同,unsigned int為4個(gè)字節(jié)瞬铸,b1占5位批幌,b2加上b1的位數(shù)之和為10位,不超過4字節(jié)嗓节,因此b2接著b1繼續(xù)存儲荧缘;
同理b3、b4拦宣、b5的類型相同截粗,位數(shù)之和不超過4字節(jié),因此接著b2繼續(xù)存儲鸵隧,總位數(shù)為25位绸罗。
由于結(jié)構(gòu)體的大小是最寬類型成員的整數(shù)倍,因此25位之后的補(bǔ)0豆瘫,直到補(bǔ)滿4字節(jié)从诲。
-----------------------------------例子2----------------------------------
(2)typedef struct? BB
{
? unsigned int b1:5;
? unsigned int b2:5;
? unsigned int b3:5;
? unsigned int b4:5;
? unsigned int b5:5;
? unsigned int b6:5;
? unsigned int b7:5;
}BB;
sizeof(BB)= 8
【解析】參考規(guī)則 1) 和規(guī)則 2) 。由于相鄰成員類型相同靡羡,unsigned int為 4 個(gè)字節(jié)(32位)系洛,當(dāng)存儲到 b7 時(shí),b7和b6之前的位數(shù)相加超過4字節(jié)略步,因此b7從新的存儲單元開始存儲描扯。即b1~b6 存儲在 第0~29位,第30趟薄、31位補(bǔ)0绽诚,b7從下一個(gè)4字節(jié)存儲單元 開始存儲5位,剩下的補(bǔ)0杭煎。
-----------------------------------例子3----------------------------------
(3)
struct test1
{
? char a:1;
? char :2;
? long b:3;
? char c:2;
};
sizeof(test1)= 12
【解析】
char a:1恩够; //用一個(gè)字節(jié)去存儲
char :2; //空域羡铲。因?yàn)榕c前面的a的類型相同蜂桶,而兩個(gè)位域的位寬相加仍然少于8位,所以依然用1個(gè)字節(jié)表示
long b:3也切; //long類型的位寬是4個(gè)字節(jié)扑媚,與前面的char類型不同腰湾,所以b與a之間偏移4個(gè)字節(jié),它們之間自動補(bǔ)充3個(gè)字節(jié)
char c:2疆股; //因?yàn)閏與b又不同型费坊,以test1中的最長的long類型的位寬進(jìn)行偏移,所以雖然char只用1個(gè)字節(jié)就夠了旬痹,但依然要占4個(gè)字節(jié)附井。
結(jié)構(gòu)體總長以最長的類型位寬做為偏移量,最長的是long型两残,占4位永毅,所以不同類型之間應(yīng)該是4個(gè)字節(jié)的偏移,即test1應(yīng)該是4字節(jié)的整數(shù)倍磕昼。 總共是12字節(jié)卷雕。
3. 指針變量的理解和定義:
1) 一個(gè)整型數(shù)
int a;
例子:
int a = 100;? //定義變量a,其值為100票从;
2)一個(gè)指向整型數(shù)的指針
int *a;
例子:
int val = 100;
a = &val; //a為一個(gè)地址變量漫雕,其值為變量val的內(nèi)存地址;內(nèi)存地址的值為100峰鄙;
3)一個(gè)指向指針的的指針浸间,它指向的指針是指向一個(gè)整型數(shù)
int **a;
例子:
int val = 100;
int *p = &val;
a = &p;? //a為地址變量,其值為指針變量p的地址吟榴,p的值為變量val的地址魁蒜;**a = *(*a) = 100;
4)一個(gè)有10個(gè)整型數(shù)的數(shù)組
int a[10];
例子:
int a[10] = {1,2,3,4,5,6,7,8,9, 0}; //有10個(gè)int類型成員的數(shù)組;
5) 一個(gè)有10個(gè)指針的數(shù)組吩翻,該指針是指向一個(gè)整型數(shù)的兜看。
int *a[10];
例子:
int *p1, *p2, *p3...., *p10;? //定義了10個(gè)整形指針變量;a為10個(gè)指針變量的數(shù)組(相當(dāng)于二維數(shù)組)狭瞎;
a[0] = p1;
a[1] = p2;
......
a[9] = p10;
6) 一個(gè)指向有10個(gè)整型數(shù)數(shù)組的指針
int (*a)[10];
例子:
int array1[10], array2[10], array3[10];?
a = array1;? //a為一個(gè)指針變量细移,指向有10個(gè)成員的一維數(shù)組;(一維數(shù)組指針)
7)一個(gè)返回值為指針的函數(shù):
int *a(int );
例子:
int *a(int value){? ///a本質(zhì)是一個(gè)函數(shù)熊锭,返回值為指針的函數(shù)弧轧;
int? *p = (int *)malloc(4);
*p = value;
return p;
}
8) 一個(gè)指向函數(shù)的指針,該函數(shù)有一個(gè)整型參數(shù)并返回一個(gè)整型數(shù)
int (*a)(int a);
例子:
int function(int val)
{
....
}
a = function;? //a為函數(shù)指針變量碗殷,類型與function函數(shù)一致精绎;
int ret = (*a)(100);? //函數(shù)運(yùn)行;
9) 一個(gè)有10個(gè)指針的數(shù)組锌妻,該指針指向一個(gè)函數(shù)代乃,該函數(shù)有一個(gè)整型參數(shù)并返回一個(gè)整型數(shù)
int (*a[10])(int);
定義了10個(gè)指針函數(shù);
例子:
int func1(int val)
{
....
}
int func2(int val)
{
....
}
a[0] = func1;? ? //a為函數(shù)指針數(shù)組从祝,可以管理多個(gè)同類型(如func1一樣)的函數(shù)襟己;
a[1] = func2;
for(int i=0; i<10; i++)
ret = (*a[i])(100);? //可以使用for循環(huán)進(jìn)行管理引谜,非常方便牍陌;
4. 字符串變量相關(guān)的地址操作:
(1)分析下列程序的結(jié)果:
--------------------例子1-----------------------------------
char *s1 = "AAA";
char s2[] = "BBB";
s1[0] = 'B';? //程序崩潰擎浴;
s2[0] = 'C';
printf ("s1:%s, s2:%s.\n", s1, s2);
【解析】
(1)s1是一個(gè)指針變量,指向一個(gè)常量字符串毒涧,即“AAA”字符串在代碼段贮预,是無法進(jìn)行修改的;
所以s1沒有可寫的內(nèi)存契讲,當(dāng)進(jìn)行賦值時(shí)仿吞,會出現(xiàn)段錯(cuò)誤:Segmentation fault (core dumped);
(2)s2是一個(gè)數(shù)組捡偏,初始化為“BBB”,默認(rèn)已經(jīng)分配了4個(gè)字節(jié)的大小唤冈,所以可以進(jìn)行修改操作:s2[0]='C',沒問題银伟;
--------------------例子2-----------------------------------
void getmemory(char *p)
{
p=(char *)malloc(100):
strcpy(p, “hello world”);
}
int main()
{
char*str=NULL;
getmemory(str);
printf(“str:%s/n”,str);
free(str);
return 0;
}
【解析】
1) 形參中值傳遞你虹,并不會改變原值的數(shù)據(jù)。因?yàn)樵分皇强截愐环輸?shù)據(jù)傳進(jìn)去彤避,修改的值是拷貝的數(shù)據(jù)傅物。
2) 指針作為參數(shù),可以傳值琉预,但無法傳地址董饰;所以指針作為形參,傳遞的是變量的地址圆米,可以看成是指針變量地址的一份拷貝卒暂,函數(shù)返回后并沒有重新指向新的內(nèi)存;仍然為空娄帖;進(jìn)入函數(shù)后也祠,指針重新指向了一塊新分配的內(nèi)存老虫,但地址已經(jīng)改變譬巫;會產(chǎn)生內(nèi)存泄露悍手;
3)如果采用雙重指針對實(shí)現(xiàn)上述功能:
? ? ? ? void getmemory(char **pptr)
{ char *p = NULL;
p=(char *)malloc(100):
strcpy(p, “hello world”);
*pptr = p;? //把p對應(yīng)的地址作為pptr的值傳出
}
int main()
{
char*str=NULL;?
getmemory(&str);? 崭倘、
printf(“str:%s/n”,str);
free(str);
return 0;
}
--------------------例子3-----------------------------------
? char szstr[10] = {0};
? strcpy (szstr,"0123456789");?
? printf ("str:%s.--\n", szstr);
解析:字符串必須要有結(jié)尾符號'\0'惩淳,不然無法識別出來乍惊,所以此程序可能會因?yàn)榈刂吩浇绯霈F(xiàn)打印亂碼濒生;
應(yīng)該定義更大一些的數(shù)組szstr[11]见秤,另外說明下佩耳,strcpy會自動把'\0'一起copy到目的地址遂蛀;
--------------------例子4----------------------------------
? char aa[10] = {0xFF, 0x0, 'a', 'b','c','d'};
? printf ("size: %d, strlen:%d. \n", sizeof(aa), strlen(aa));
【解析】
1)sizeof 計(jì)算變量的實(shí)際大小,所以sizeof(aa) = 10干厚;
2) strlen 原理是遇到‘\0’或0馬上結(jié)束李滴,所以上述strlen(aa) = 1;
--------------------例子5-----------------------------------
? void swap_data( int *p1, *p2)
? {
? int *p;
? *p = *p1;
? *p1 = *p2;
? *p2 = *p;
? }
【解析】
運(yùn)行結(jié)果程序崩潰螃宙,原因是定義的p變量為指針類型,但未有分配地址空間所坯,所以*p = *p1,直接賦值會出錯(cuò)谆扎;
--------------------例子6-----------------------------------
說明以下變量對應(yīng)的地址和值是多少?
#include <stdio.h>
int main()
{
int array[5] = {1, 2, 3, 4, 5};
printf("array = %p, value:%d.\n", array, *(array));
printf("array + 1 = %p, value:%d.\n", array + 1, *(array+1)));
printf("array + 2 = %p, value:%d.\n", array + 2, *(array+2)));
printf("array + 3 = %p, value:%d.\n", array + 3, *(array+3)));
printf("&array = %p\n", &array);
printf("&array + 1 = %p\n", &array + 1);
printf ("sizeof(array):%d, sizeof(&array):%d.\n", sizeof(array), sizeof(&array));
int *ptr = (int *)(&array + 1);
printf ("*(array+1)=%d, *(ptr-1)=%d \n", *(array+1), *(ptr-1));
return 0;
}
【解析】(輸出結(jié)果):
array = 0x7ffe96cce190, value:1. //以為e190結(jié)尾的地址芹助;
array+1 = 0x7ffe96cce194, value:2. //以為e190+4結(jié)尾的地址堂湖;
array+2 = 0x7ffe96cce198, value:3. //以為e190+8結(jié)尾的地址;
array+3 = 0x7ffe96cce19c, value:4. //以為e190+12結(jié)尾的地址状土;
&array = 0x7ffe96cce190 //以為e190結(jié)尾的地址
&array + 1 = 0x7ffe96cce1a4
//!!重點(diǎn):以為e190+20結(jié)尾的地址,其中20整個(gè)數(shù)組的長度大小无蜂。
所以,&array + 1 表示:以整個(gè)數(shù)組大小為單位進(jìn)行計(jì)算操作的蒙谓;
sizeof(array):20, sizeof(&array):8.
//array是一個(gè)數(shù)組變量斥季,大小為數(shù)組長度;&array是一個(gè)指針變量累驮,大小為8個(gè)字節(jié)(64位系統(tǒng))
*(array+1)=2, *(ptr-1)=5
//這里注意ptr地址是array+sizeof(array), 所以ptr-1就是指向最后一個(gè)數(shù)據(jù)地址酣倾,址為5;
5. 指針與地址轉(zhuǎn)換問題:
--------------------例子1 ---------------------------
? unsigned char *p1 = NULL;
? ? ? unsigned long *p2 = NULL;
? ? ? unsigned int? address = 0x0801000;
? ? ? p1 = (unsigned char *)address;
? ? ? p2 = (unsigned long *)address;
? ? ? printf ("p1+1: %p, p2+1:%p.\n", p1+1, p2+1);
? ? ? printf ("p1+5: %p, p2+5:%p.\n", p1+5, p2+5);
請說明下打印結(jié)果的慰照。
【結(jié)果】
p1+1: 0x801001, p2+1:0x801008. //char類型占一個(gè)字節(jié)灶挟, long類型占8個(gè)字節(jié);
p1+5: 0x801005, p2+5:0x801028.
【解析】
定義不能類型的指針毒租,在進(jìn)行加減運(yùn)算時(shí)會按指針類型的字節(jié)數(shù)進(jìn)行操作稚铣。
- -------------------例子2---------------------------
int array [5] = {1,2,3,4,5};
int *ptr = (int *)(&array? + 1);
printf ("%d, %d \n", *(array +1), *(ptr-1));
【結(jié)果】?2, 5
【解析】?&array + 1 表示:以整個(gè)數(shù)組大小為單位進(jìn)行計(jì)算操作的;
所以ptr指向地址為:array +sizeof(array ) = array +5墅垮,那么 *(ptr-1)即為array [4]的值 惕医;
-------------------例子3-----------------------------
void foo(void)
{
unsigned int a = 6;
int b = -20;
(a+b > 6) ? puts("> 6") : puts("<= 6");
}
【結(jié)果】這個(gè)問題測試你是否懂得C語言中的整數(shù)自動轉(zhuǎn)換原則。這無符號整型問題的答案是輸出是 ">6"算色。
【解析】?是當(dāng)表達(dá)式中存在有符號類型和無符號類型時(shí)所有的操作數(shù)都自動轉(zhuǎn)換為無符號類型抬伺。
因此-20變成了一個(gè)非常大的正整數(shù),所以該表達(dá)式計(jì)算出的結(jié)果大于6灾梦。
這一點(diǎn)對于應(yīng)當(dāng)頻繁用到無符號數(shù)據(jù)類型的嵌入式系統(tǒng)來說是豐常重要的峡钓。
---------------------例子4 -------------------------------
1)如何把地址為 0x100000的內(nèi)存,賦值為0x1234;
2)如何跳轉(zhuǎn)到地址為 0x100000的函數(shù)若河,并運(yùn)行能岩;
【解析】
1)(unsigned int*)0x100000 = 0x1234;
2)步驟如下:
---首先要將0x100000強(qiáng)制轉(zhuǎn)換成函數(shù)指針即: (void(*)())0x100000
---然后再調(diào)用它: *( (void(*)())0x100000) ();
6. 變量自加的問題:
--------------------例子1 ---------------------------
#include <stdio.h>
int main
{
int a,b,c,d;
a=10;
b=a++;? ? ? //1. a先把值賦給b,然后自加萧福;所以:b=10,a=11;
c=++a; //2. a先自加后拉鹃,再把值賦給C; 所以:a=12,c=12;
d=10*a++; //3. a先乘以10賦給d, 然后a自加;所以:a=13, d=120;
printf("a, b,c膏燕,d: %a, %d,%d,%d", b, c,d);
return 0;
}
【結(jié)果】a=13, b=10钥屈,c=12,d=120
--------------------例子2 ---------------------------
int a = 5, b = 7, c, d;c = a+++b;
分析:重點(diǎn)是理解運(yùn)算的優(yōu)先級坝辫,++ 優(yōu)先級高于+篷就,所以先進(jìn)行a++運(yùn)算,再進(jìn)行+運(yùn)算阀溶;
可看成:c = a++ + b;
【答案】這段代碼持行后a = 6, b = 7, c = 12
---------------------例子3 -------------------------------
代碼如下:
? int b=10;
? ? printf("1-----(b++, b++): (%d, %d)\n", b++,b++);
? ? b=10;
? ? printf("2-----(++b, ++b): (%d, %d)\n", ++b,++b);
? ? b=10;
? ? printf("3-----(b++, ++b): (%d, %d)\n", b++,++b);
? ? b=10;
? ? printf("4-----(++b, b++): (%d, %d)\n", ++b,b++);
【結(jié)果】
1-----(b++, b++): (11, 10)
2-----(++b, ++b): (12, 12)
3-----(b++, ++b): (11, 12)
4-----(++b, b++): (12, 10)
【解析】printf函數(shù)參數(shù)中的變量是經(jīng)過壓棧和出棧操作的腻脏,參數(shù)中括號內(nèi)的表達(dá)式是從右向左執(zhí)行的鸦泳,輸出結(jié)果是從左向右的:
1. 先執(zhí)行右邊的b++银锻,因?yàn)閎++是先賦值保存,后++做鹰,所以cpu會記住b的值(即右邊打印值已經(jīng)確定即為10)击纬,之后再執(zhí)行左邊的b++,此時(shí) b為11(執(zhí)行前面b++得到的結(jié)果)钾麸,因?yàn)橛抑?+更振,因此就輸出為11,所以結(jié)果為11饭尝, 10肯腕;
2. 先執(zhí)行右邊的++b,因?yàn)?+b先自加钥平,再調(diào)用实撒,而此時(shí)cpu并沒有到調(diào)用它的時(shí)候,printf第二個(gè)格式化的值對應(yīng)的數(shù)字現(xiàn)在還不確定涉瘾, 再然后執(zhí)行++b知态,同樣先自加,再調(diào)用的左值運(yùn)算立叛,即輸出12负敏,接著再輸出第二個(gè),因?yàn)閏pu沒有記住對第二的調(diào)用秘蛇,他會找當(dāng)前b的值其做,當(dāng)前b的值是12,所以輸出12, 12赁还;
3. 先執(zhí)行右邊的++b妖泄,之后b=11,同樣秽浇,第二個(gè)打印參數(shù)未確定浮庐,再執(zhí)行b++,因?yàn)閎++是先調(diào)用,再自加审残,所以先輸出b梭域,此時(shí)b的值是11,再自加搅轿,b=12病涨,此時(shí)再輸出b,因此輸出的結(jié)果是11,12璧坟;
4. 同理分析既穆,右邊的b++先執(zhí)行,由于是右值運(yùn)算雀鹃,此時(shí)第二個(gè)打印值 已經(jīng)確定幻工,保存下來了,即為10黎茎, 再進(jìn)行左邊的++b運(yùn)算囊颅,打印值為12,即得為:12傅瞻, 10踢代。