C语言,你真的弄懂了么?
程序(來源 ):
?
#include <stdio.h>int main(void) {int x[4];printf("%p\n", (void*) (x));printf("%p\n", (void*) (x + 1));printf("%p\n", (void*) (&x));printf("%p\n", (void*) (&x + 1)); }?假設(shè)x的地址為n,那么輸出為:
?
n n+4 n n+16?window下使用gcc編譯輸出結(jié)果:
?
0x22cd44 0x22cd48 0x22cd44 0x22cd54?前三個還比較好理解,最后一行中&x實(shí)際表示是一個類型為int (*)[4]類型的指針,所以&x+1后地址增加16。
?有一個和上面類似的程序(源自《C語言深度剖析》,可以在網(wǎng)上搜索下載到):
#include <stdio.h>int main(void) {int a[] = {1,2,3,4,5};int *p = (int *)(&a+1);printf("%d %d\n",*(a+1),*(p-1));return 0; }? 此程序輸出結(jié)果為2,5
類似的還有個程序:
#include <stdio.h>int main(void) {int a[] = {1,2,3,4,5,6,7,8,9,10};int (*p1)[3] = &a;int (*p2)[4] = a;printf("%d %d\n",*(*(p1+1)+1),*(*(p2+2)+1));return 0; }? 該程序編譯時會提示警告(p1和p2初始化采用不兼容的指針類型)。
? 這里,p1和p2都是數(shù)組指針。p1指向有三個元素的整型數(shù)組,p2指向有四個元素的整形數(shù)組。
? 數(shù)組指針p1類似于一個二維數(shù)組b[][3],而根據(jù)二維數(shù)組b[i][j]可以表示成*(*(b+i)+j)的形式。所以*(*(p1+1)+1)相當(dāng)于二維數(shù)組b[][3]中的b[1][1],因而對應(yīng)于a[4],所以輸出結(jié)果為5,類似的可以得出另一個指針的輸出結(jié)果。
所以整個輸出結(jié)果為5,10
同樣,還有個程序:
#include <stdio.h> int main(void) {int a[4] = {1,2,3,4};int *ptr1 = (int*)(&a+1);int *ptr2 = (int *)((int)a+1);printf("%x,%x\n",ptr1[-1],*ptr2);return 0; }? ptr1[-1]的值(跟上面的例子的情況類似)為4
? 而*ptr2的值則根據(jù)處理器的不同而可能有不同的結(jié)果(參見大端模式和小端模式 endianness )
? 如果為小端模式(例如intel x86兼容處理器,8051,avr等),那么*ptr2(注意使用題目中使用了%x輸出格式)輸出結(jié)果為2000000
? 如果為大端模式(例如motorola 68k,powerpc,IBM sys/360等),那么*ptr2輸出結(jié)果為100
判斷處理器使用什么模式,可以使用下面函數(shù)進(jìn)行檢測(相關(guān)鏈接 ):
/*little endian when return 1,else big endian*/ int CheckEndian() {union{int i;char c;}e;e.i = 1;return (e.c == 1); }? 如果使用大端模式,那么數(shù)組a在內(nèi)存中表示(十六進(jìn)制)為 00 00 00 01 00 00 00 02 00 00 00 03 00 00 00 04
ptr2指向第二個00,所以為00 00 01 00(即0x100)
? 如果是小端模式,那么數(shù)組a在內(nèi)存中表示為01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00,ptr2指向第一個00,所以其指向內(nèi)容為00 00 00 02,由于使用了小端模式,所以需要顛倒過來表示即02000000(也即0x2000000).
接著是一些二維數(shù)組和二級指針的一些例子(同樣源自《c語言深度剖析》):
?
#include <stdio.h>int main(void) {int a[3][2]={(1,2),(3,4),(5,6)};int *p;p = a[0];printf("%d\n",p[0]);return 0; }?編譯后運(yùn)行,結(jié)果為2,因為圓括號內(nèi)的使用了逗號表達(dá)式,二維數(shù)組a的初始化相當(dāng)于int a[3][2]={2,4,6};
?
#include <stdio.h> int main(void) {int a[5][5];int (*p)[4];p = a;printf("a_ptr=%#p,p_ptr=%#p\n",&a[4][2],&p[4,2]);printf("%p,%d\n",&p[4][2]-&a[4][2],&p[4][2]-&a[4][2]);return 0; }?編譯后運(yùn)行,結(jié)果為
a_ptr=0X0022FF70,p_ptr=0X0022FF38 FFFFFFFC,-4?這是因為二維數(shù)組實(shí)際上仍然用一維數(shù)組來表示。而int (*p)[4]相當(dāng)于把a(bǔ)的一維數(shù)組表示又轉(zhuǎn)化成二維數(shù)組[][4],這樣&p[4][2]相當(dāng)于p+4*4+2,&a[4][2]相當(dāng)于p+4*5+2,所以二者相減結(jié)果為-4.
?
接著是幾個內(nèi)存分配的程序(源自《高質(zhì)量程序設(shè)計指南--c++/c語言》)
(1)
#include <stdio.h>void getmemory(char *p) {p = (char*)malloc(100*sizeof(*p));} int main(void) {char *str = NULL;getmemory(str);strcpy(str,"hello,world");printf("%s\n",str);return 0; }? 編譯運(yùn)行后程序會發(fā)生崩潰,因為getmemory只是將NULL值傳給參數(shù)p,然后又給p分配了100個字節(jié)空間,對str沒有任何改變。由于str仍未NULL,所以對空串進(jìn)行串拷貝會發(fā)生崩潰。
(2)
#include <stdio.h>char *getmemory(void) {char p[] = "hello,world";return p; } int main(void) {char *str = NULL;str = getmemory();printf("%s\n",str);return 0; }?編譯運(yùn)行該程序,其輸出結(jié)果為亂碼。
這是因為C語言中棧幀布局可知,getmemory被調(diào)用后,會在棧上分配數(shù)組p來存放"hello,world"字符串并返回該串地址,但是在getmemory返回后,在棧上分配的數(shù)組部分已經(jīng)變成無效狀態(tài),此時調(diào)用printf函數(shù),就會覆蓋掉原來數(shù)組p中的內(nèi)容。但是輸出串地址仍是以前的地址,所以可能輸出亂碼。
(3)
#include <stdio.h>void getmemory(char **p, int num) {*p = (char*)malloc(num); } int main(void) {char *str = NULL;getmemory(&str,100);strcpy(str,"hello,world");printf("%s\n",str);return 0; }?編譯運(yùn)行該代碼會輸出”hello,world",但是該程序沒有將分配空間釋放,所以可能會產(chǎn)生內(nèi)存泄漏
(4)
#include <stdio.h>void test(void) {char *str = (char*)malloc(100);strcpy(str,"hello");free(str);if(str != NULL){strcpy(str,"world");printf("%s\n",str);} } int main(void) {test();return 0; }?編譯運(yùn)行該程序后,可能產(chǎn)生非常危險的后果。因為前面給str分配空間并釋放,但是并沒有將str設(shè)置為NULL,因而str成為“野指針”,下面還要繼續(xù)對str原來位置復(fù)制一個串"world"并輸出,這就成了篡改堆中內(nèi)容,可能帶來非常嚴(yán)重的后果。
?
?
接下來是一個要求不用循環(huán)和條件語句輸出1到1000的所有整數(shù)(來源 )。
(方法1):該方法會產(chǎn)生一個錯誤(除0故障),但能輸出正確結(jié)果
?
#include <stdio.h> #define MAX 1000 int boom; int foo(n) {boom = 1 / (MAX-n+1);printf("%d\n", n);foo(n+1); } int main() {foo(1); }?(方法二):
?
#include <stdio.h> #include <stdlib.h>void f(int j) {static void (*const ft[2])(int) = { f, exit };printf("%d\n", j);ft[j/1000](j + 1); }int main(int argc, char *argv[]) {f(1); }?這段代碼可以簡化為:
?
#include <stdio.h> #include <stdlib.h>void main(int j) {printf("%d\n", j);(&main + (&exit - &main)*(j/1000))(j+1); }? 運(yùn)行此程序時,由于不帶任何參數(shù),所以j的初始值為1(相當(dāng)于argc參數(shù),只是這里變量名換一下而已,不影響程序的執(zhí)行),然后輸出1,下一句中j/1000值為0(j為1-999之間任意整數(shù)時其值均為0),所以相當(dāng)于執(zhí)行(&main)(2),這是一個函數(shù)指針調(diào)用,然后輸出2,繼續(xù)執(zhí)行下去直至j為999時,會調(diào)用(&main)(1000),此時輸出1000,j/1000值變?yōu)?,所以下一步調(diào)用(&main+(&exit-&main))(1001),即exit(1001),此時使用exit跳出函數(shù)的執(zhí)行。
?
?
不使用中間變量交換兩個整型變量的值,代碼如下:
int x,y; x=x^y; y=x^y; x=x^y;?
Duff's device :
?
send(to, from, count)register short *to, *from;register count;{register n=(count+7)/8;switch(count%8){case 0: do{ *to = *from++;case 7: *to = *from++;case 6: *to = *from++;case 5: *to = *from++;case 4: *to = *from++;case 3: *to = *from++;case 2: *to = *from++;case 1: *to = *from++;}while(--n>0);}}?這個代碼格式是老的代碼格式。具體講解見上面的鏈接。
?
檢查一個字符串(名稱為s1)是否是另外一個字符串(名稱為s2)的旋轉(zhuǎn)版本(來源 )。例如"stackoverflow"的旋轉(zhuǎn)版本字符串有: "tackoverflows",“overflowstack"等。
算法實(shí)現(xiàn)方法如下:
(1)確定兩個串長度相等
(2)將s1和s1連接起來,檢查s2是否是連接后的串的字串
#include <stdio.h> #include <stdlib.h> #include <string.h> int IsRotation(char s1[], char s2[]) {int len1 = strlen(s1),len2=strlen(s2);char *str = malloc((len1+len1+1)*sizeof(char));if(len1 != len2)return 0;if(str == NULL){fprintf(stderr,"error while allocating space\n");return -1;}if(strcpy(str,s1) == NULL || strcat(str,s1) == NULL){fprintf(stderr,"error while copying or concatenate string s1 to str\n");return -1;}if(strstr(str,s2) != NULL)return 1;return 0; }?這段代碼是我自己寫的,可能有不完善的地方。當(dāng)執(zhí)行中出錯時返回-1,如果是旋轉(zhuǎn)串則返回1,否則返回0.
總結(jié)
以上是生活随笔為你收集整理的C语言,你真的弄懂了么?的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python3、sqlmap下载与安装教
- 下一篇: Python界面 PyQT可视化开发(p