1.指針是一種數(shù)據(jù)類型需纳,裝地址的數(shù)據(jù)類型。注意指針類型(對偏移艺挪,內(nèi)存操作理解很重要)候齿,什么樣的類型決定了操作空間的大小。地址加減闺属,就是加減一個類型的大小慌盯。*代表間接訪問運算符。
#include<stdio.h>
main()
{
//int a = 12;
//int *p = &a;//理解:首先聲明一個指針類型的變量p掂器、再取a的地址賦值給p亚皂。這里的*,起標記作用国瓮,標記p是一個指針變量
//p = &a;
//printf("% p %p %p %d\n", p, &a,&p,*p);//&p取p的地址灭必,與前兩個不同狞谱。p裝的是a的地址.這里*是間接訪問運算符或內(nèi)存操作運算符
//a = 14;
//printf("%d\n", *p);//14 改變指向變量的值,也改變了指針的值
//*p = 145;
//printf("%d", a);//145 改變指針的值禁漓,也改變了指向變量的值
int a = 12;
int *p = &a;//切記:p是一塊空間跟衅,有他自己的地址
int *p3 = p;
printf("%d\n", *p3);//輸出12 ,p代表a的空間地址播歼,*p3獲取的就是a的值
//證明不同
//int *p1 = &p;// 報錯:“p1” : “int *”與“int **”的間接尋址級別不同
int **p1 = &p;//二級指針伶跷。、秘狞,裝一級指針地址
*p1 == p;//此時兩者的關(guān)系 true
*p == a;
getchar();
return 0;
}
2.指針與數(shù)組二級指針與數(shù)組無關(guān)叭莫,涉及到數(shù)組與指針,利用好數(shù)組就是地址+[偏移量]的思維烁试。指針數(shù)組(地址數(shù)組)(例:int p[5]):用數(shù)組裝入地址的雇初;數(shù)組指針(例:int (p)[5]):數(shù)組類型的地址**
#include<stdio.h>
int main(void)
{
int a[5] = { 9,6,4,5,4 };
int *p = &a[0];
printf("%p %p %p %p %p\n", p,p+1,p+2,p+3,p+4);//運算完p沒有改變,用多條語句++p,也可以實現(xiàn)改變地址减响,但是會引起p的改變
printf(" %p %p\n", &*(p+2), &p[2]);
int i ;
for ( i = 0; i < 5; i++)
{
printf("%p\n", *(p + i));
//printf("%p\n", *p++);//注釋上一句結(jié)果同上9 6 4 5 4靖诗,此語句p++改變了p的指向
printf("%d ,%d\n",a[i],p[i]);//指針的下標運算(指定首地址,且為連續(xù)的空間)支示,兩者結(jié)果相同 (地址+[偏移量])刊橘。
}
printf("%p %p %p %p %p %p\n", a, &a[0], &a[1], &a[2], &a[3], &a[4]);//a也是首地址,上下兩者輸出一樣悼院。
//**指針數(shù)組
int a1 = 1, b1 = 2, c1 = 3;
int *arr[3] = { &a1,&b1,&c1 };
printf("%d %p\n", *arr[1], arr[1]);//分別用來修改值,修改地址
//指針數(shù)組的拉鏈結(jié)構(gòu)
int b11[3] = { 1,2,3 };
int c11[2] = { 2,5 };
int d11[4] = { 1,3,2,44 };
int e11[5] = { 45,6,5,4,26 };
int f11[2] = { 4,9 };
int *a11[5] = { b11,c11,d11,e11,f11 };
printf("a11:%d\n",a11[2][2]);//二維數(shù)組的方式訪問咒循,但是二維數(shù)組地址是連續(xù)的据途,一般拉鏈結(jié)構(gòu)地址不連續(xù),相互分開不同叙甸。大小不同但類型要同
//**數(shù)組指針
int a2[5] = { 3,4,2,6 ,8};
int*p2 = &a2[4];
//int *p22 = &a;//類型不兼容報錯颖医,根據(jù)報錯找類型 warning C4047: “初始化”:“int *”與“int (*)[5]”的間接級別不同
//下標不一樣,類型是不同的裆蒸。int [6] int [5]不同類型的熔萧,可用上句測試
int(*p22)[5] = &a2;
//數(shù)組指針使用
p22[2];//為a2數(shù)組的地址,再偏移2(2個a2數(shù)組的大小,2*5*4)
printf("第二個:%d\n", (*p22)[2]);//輸出2僚祷,取值設(shè)置等操作
printf("%p %p\n", &(*p22)[2],&a2[2]);
getchar();
return 0;
}
3.二維數(shù)組與指針佛致。聲明的指針指向哪個地址,操作的就是對應(yīng)層次的地址辙谜。一個指針指向一個變量俺榆,p對應(yīng)他本身(若p=&a[0][0]即*p==a[0][0])。指針的大小取決于編譯環(huán)境装哆。32位編譯環(huán)境下的全為4字節(jié)罐脊,64位位8字節(jié)(vs編譯器可以切換)定嗓。
#include<stdio.h>
int main(void)
{
//int a = 12;
//int *p = a;//警告:“int *”與“int”的間接級別不同
//printf("%p %d\n", p, *p);//輸不出值
int a[2][3] = { {2,5,3} ,{3,5,3} };
int *p = &a[0][0];//==a[0]
for (int i = 0; i <= 5; i++)
{
printf("%d\n",*(p+i));//可以全部輸出,因為存的空間是連續(xù)的
}
int (*p1)[3] = &a[1];
int(*p2)[2][3] = &a;//對比a[2][3]發(fā)現(xiàn)數(shù)組指針與數(shù)組一種形式上的關(guān)系萍桌。但是每一種聲明的方式宵溅,指向?qū)哟味际窍嗤摹上炎?梢院蜕弦痪鋵Ρ? int i, j;
for ( i = 0; i < 2; i++)
{
for (j = 0; j < 3; j++)
{
printf("%d\n", (*p2)[i][j]);
}
}
getchar();
return 0;
}
4.堆區(qū)和棧區(qū) 內(nèi)存分為:棧區(qū)(由系統(tǒng)申請)恃逻,堆區(qū)(由自己申請釋放),全局區(qū)反症,字符常量區(qū)辛块,代碼區(qū)。malloc申請一段連續(xù)的指定大小的空間铅碍,并返回首地址润绵。
#include<stdio.h>
#include<stdlib.h>//兩個都是malloc/calloc的庫文件
#include<malloc.h>
#include<memory.h>
int main(void)
{
//void * maloc(size_t size)
int *p = (int*)malloc(4);//參數(shù)為字節(jié)數(shù)(除4得一,代表只有一個數(shù)),將空間標記為int*類型胞谈。4默認轉(zhuǎn)為無符號的了尘盼,或者寫成4u
p = (int*)malloc(4);//再申請的話會造成空間丟失,內(nèi)存泄漏烦绳。但還是被占用的
//地址的類型決定了卿捎,地址每次訪問的大小。size_t==unsigned int
//申請不到失敗径密,返回NULL
*p = 2;//賦值
//空間賦值午阵,字節(jié)設(shè)置值
memset(p, 0, 4);//memcpy才是數(shù)組賦值。參數(shù)1為賦值變量享扔,參數(shù)二所賦的值底桂,參數(shù)三字節(jié)數(shù)
if (NULL==p)
{
printf("申請失敗>迕摺籽懦!");
}
int a = sizeof(size_t);//32位編譯器環(huán)境是4字節(jié),64位是8字節(jié)
printf("%p\n",p);
free(p);//釋放空間氛魁,把空間還給操作系統(tǒng)
printf("%p\n", p);//釋放后p就是野指針了(訪問受限)暮顺。還有就是定義指針為初始化也是野指針
//malloc與數(shù)組
int *p1=(int*)malloc(sizeof(int) * 5);
int a1[5];
//int *p2 = &a1[0];
//p1 = p2;
/*for (int i = 0; i <= 5; i++)
{
printf("%d\n",*(p+i));
}*/
//free(p2);
//數(shù)組類型
int (*p2)[5]=(int(*)[5])malloc(sizeof(int) * 5);
(*p2)[5] = &a1;
printf("比較:%d\n",*p2==a1);//0
printf("比較1:%d\n", *p2 == a1);//1
for (int i = 0; i < 5; i++)
{
printf("%d\n", *( p2)[i]);
}
free(p2);
int(*p3)[2][5] = (int(*)[2][5])malloc(sizeof(int) * 5);
//calloc
int*p4=(int*)calloc(5, 4);//5個元素,每個4字節(jié);申請數(shù)組比較方便
for (int i = 0; i < 5; i++)
{
printf("%d\n",p4[i]);//區(qū)別之一秀存,全部初始化為0
}
free(p4);
//realloc重新修改字節(jié)
printf("%d\n", _msize(p));//_msize()查看malloc出的空間字節(jié)大小
int *p5 = (int*)realloc(p, 20);//重新分配20字節(jié)
getchar();
return 0;
}
5.函數(shù)基本使用
#include<stdio.h>
#include<malloc.h>
void fun1();//函數(shù)聲明:先聲明捶码,函數(shù)體可以放最后
int fun2();
int fun4(int a, float b);
int fun5(int *p);
void fun6(int **p);
void fun(void)//無返回值,無參數(shù)或链。 整體放在最后面調(diào)用報錯宙项。使用函數(shù)聲明來解決
{
printf("這是一個函數(shù)!");
return;//終止函數(shù)
}
//返回多個值
int * fun3(void)
{
int *p = (int*)malloc(8);
*p = 4;
p[1] = 5;
return p;//返回指針
//int p1[2] = { 4,5 };//雖然表面輸出沒問題,這是c自身的bug株扛。會有警告
//return p1;//返回棧區(qū)尤筐,這里p1為棧區(qū)局部變量汇荐,運行完函數(shù)后,空間就被釋放了盆繁,使用的是非法空間
}
int main(void)
{
//一般函數(shù)的執(zhí)行略慢一丟丟于未封裝的代碼掀淘。因為涉及到跳轉(zhuǎn)
fun();//調(diào)用:函數(shù)地址+參數(shù)列表
printf("%p %d %d\n", fun,&fun,fun==&fun);
(&fun)();//結(jié)果同上,不要忘記前面的小括號油昂。
void(*p)(void) = fun;//函數(shù)指針
fun1();
printf("返回值:%d\n",fun2());
//返回多個值函數(shù)測試
int*a = fun3();
printf("%d %d\n",a[0],a[1]);
free(a);//記得釋放堆空間
//通過函數(shù)修改外部值革娄,思路修改地址,把地址傳進函數(shù)
int b = 2;
int *p1 = &b;//p1代表(裝載的)b的地址冕碟,p1自己還有一個自己空間的地址
fun5(p1);
//通過函數(shù)修改指針變量
fun6(&p1);
getchar();
return 0;
}
void fun1(void)//函數(shù)定義
{
printf("這是一個fun1拦惋!");
}
int fun2(void)
{
printf("無參有返回值函數(shù)!");
return 4;
}
int fun4(int a,float b)
{
printf("兩參數(shù) %d %d\n",a,b);
return 4;
}
int fun5(int *p) {
*p = 24;
printf("xiugao的%d\n",*p);
}
void fun6(int **p) {
*p = NULL;
}
5.較為特殊的函數(shù)和用法(數(shù)組參數(shù)安寺,遞歸函數(shù))厕妖,遞歸注意遞歸控制變量
#include<stdio.h>
#include<stdarg.h>
//一維數(shù)組
void fun(int *p,int length)//int *p改為 int p[]([]可以寫任何整數(shù)字),數(shù)組做形參會被解析成指針
{
for (int i = 0; i < length; i++)
{
printf("%d\n",p[i]);
}
}
//二維數(shù)組
void fun1(int (*p)[3],int hang,int lie)//同一維的改寫挑庶,參數(shù)int p[任意][3]
{
for (int i = 0; i < hang; i++)
{
for (int j = 0; i < lie; i++)
{
printf("%d\n",p[i][j]);
}
}
}
//函數(shù)類型
int fun2() {}
int fun3(int a) {}
void fun4(int a) {};
//遞歸函數(shù)
int fun5(int n)
{
if (n==1 || n==2 || n==0)
{
return 1;
}
else {
return fun5(n - 1) + fun5(n - 2);
}
}
//指定未知參數(shù)個數(shù)a
void fun5(int a,...)
{
va_list ap;//定義參數(shù)數(shù)組
va_start(ap,a);//將參數(shù)裝進數(shù)組
printf("%d\n", va_arg(ap, int));//將數(shù)取出來言秸,就沒了。再取就是 順序往下取第二個
printf("%lf\n", va_arg(ap, double));
printf("%d\n", va_arg(ap, int));
}
int main()
{
int a[5] = { 1,2,3,4,5 };
fun(a, 5);
int a1[2][3] = { {1,2,3},{4,5,6} };
int(*p)[3] = a1;
fun(a1,2,3);
//函數(shù)地址 地址類型:有返回值類型迎捺,參數(shù)類型個數(shù)決定
//int ();對應(yīng)的類型去掉名字举畸,保留參數(shù)
//int (int a);
int(*p1)(int a) = fun3;//int a對應(yīng)fun3的參數(shù)列表
//int (*p1)(int a) = fun4;//也是可以的
//遞歸斐波拉契數(shù)列,通項公式型
printf("遞歸:%d\n", fun5(6));
//多參數(shù)
fun5(3, 12, 34.34 , 78);//3是指參數(shù)個數(shù)
getchar();
return 0;
}
6.字符
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int main(void)
{
printf("%c\n",'A');//要用''凳枝,不然編譯器會吧字母當做變量
printf("%c\n", 64);//輸出字符@
printf("%d\n", 'A');//65
putchar('a');//輸出
putchar('\n');
char a = 66;//1字節(jié)
printf("%c\n", a);//B
//scanf我們在控制臺的輸入抄沮,系統(tǒng)會開辟一塊空間(輸入緩沖區(qū))存儲我們的所有輸入,按下回車結(jié)束
int d, f;
scanf("%d", &d);//從緩沖區(qū)拿數(shù)據(jù)岖瑰,輸入時2 3連續(xù)輸叛买,中間帶空格可正常輸出《Щ罚回車后聪全,這時scanf就去緩沖區(qū)取相應(yīng)類型的數(shù)據(jù)
scanf("%d", &f);//去緩沖區(qū)讀取數(shù)據(jù)
printf("%d %d\n", d, f);
//清空緩沖區(qū) setbuf(stdin,NULL)(c標準)和fflush(stdin)(不標準泊藕,有些編譯器不支持)還有while((c=getchar())!='\n'&&c!=EOF);
setbuf(stdin,NULL);
char e, g;
scanf("%c", &e);//字符類型的輸入辅辩,回車和空格對scanf的讀取有影響,輸入一個字母回車娃圆。此時相當于存儲了字母1和\n(回車)
scanf("%c", &g);//一般直接輸入兩個相連例如ab
printf("%c %c\n", e, g);//d存儲為輸入的字母玫锋,f存儲的是\n
//scanf_s讀取格式
scanf_s("%c \n", &e, 1);//1表示字節(jié)數(shù),多個輸入加多個指定的字節(jié)數(shù)
//char h =_getch();//隨輸入隨讀取讼呢,不需要回車確認撩鹿。頭文件conio.h
char c1 = 0;//轉(zhuǎn)換大小寫
while (1)
{
scanf("%c", &c1);
if(c1>=65&&c1<=90)
{
printf("%c",c1+32);
}
else if (c1 >= 97 && c1 <= 122)
{
printf("%c", c1 - 32);
}
else if (c1 == '\n')
break;
}
//字符數(shù)組
char arr[5] = { 'r','g','g','r','r' };
getchar();
return 0;
}
7.字符串(以\0 (0)結(jié)尾的字符數(shù)組)
#include<stdio.h>
#include<string.h>
int main(void)
{
char str[3] = { 'a','b','e' };//這是純字符數(shù)組
char str1[3] = { 'a','b','\0' };//這就是字符串了
printf("%s\n", str);//輸出亂碼,因為結(jié)束不了
printf("%s\n", str1);//輸出ab(遇到\0(0)結(jié)束悦屏,\0轉(zhuǎn)義為0)节沦,不輸出\0键思。用%c可以輸出\0
printf("%s\n", "dfaf 522!!");//常量字符串,自帶\0(dfaf 522!!\0)
char *p = "aaa555 55";//雙引號返回字符串的首地址
char *pp = 'a';//
printf("指針輸出字符%c\n", *p);
printf("指針輸出字符地址%p \n", p);
//*p = 'w';//崩了,內(nèi)存地址指向非法
//p [0]= 'w';//報錯:寫入位置 0x00966B44 時發(fā)生訪問沖突甫贯。p指向的是一個常量區(qū)字符串地址吼鳞,無法再進行修改。
//p = "wfwfgwg";//ok
//printf("指針輸出字符串%s\n", *p);//直接炸掉
printf("指針輸出字符串%s\n", p);
printf("查看*p %c\n", *p);//輸出w
printf("我也可以輸出=懈椤赔桌!");//定義中第一個參數(shù)為char* 而""剛好返回地址
char str11[15] = "1546 eege";//常量字符串不能修改。字符串在棧區(qū)數(shù)組和常量區(qū)都有
str11[1] = 'w';//改變的是棧區(qū)的
char str2[] = "vg1546 eege";//不用考慮越界
//str1[10]="wdqwfq";錯誤的不能一次轉(zhuǎn)入渴逻。通過循環(huán)可以
char *p1 = "de";
int i = 0;
for (; *p!='\0'; p++)
{
str2[i] = *p;
i++;
}
str2[i] = '\0';//給結(jié)尾加個\0疾党,不然就是替換前兩個而已。
strcpy(str2, str11);//將str11賦值給str2(str2空間要足夠大)
printf("%s\n", strcpy(str2, str11));
//strcpy()//類比于scanf_s(str,10,str1)多了個指定(參數(shù)1的)字節(jié)數(shù)
strncpy(str2, "fwfwgwg", 3);//3表示自從字符串中取三字節(jié)到str2中
strncpy_s(str2,10, "fwfwgwg", 3);//多了個指定參數(shù)1的字節(jié)數(shù)
char str22[20];
scanf("%s", str22);//輸入字符串后面默認加了\0,輸入的空格相當于一個分割符,不讀空格惨奕。
//scanf("%s", str22,19);//保留一個字節(jié)給系統(tǒng)輸入\0
//gets(str22);//讀入全部雪位,包括空格
//gets_s(str22,19);//總結(jié)_s系列的函數(shù),主要優(yōu)化了越界處理和報錯墓贿,方便查找茧泪。在本句就報錯,不再程序執(zhí)行完在報錯
printf("%s",str22);
char str3[3] = "譚";//字符串裝入漢字兩個字節(jié)聋袋,另外后面有個\0;或者百度漢字國標碼
//求字符串長度
size_t a = strlen("156466");//只讀取字符數(shù)队伟,不讀取\0. size_t無符號整形重命名
//字符串比較,切記與字符串長短無關(guān)幽勒,按順序比較asc2碼大小
int result = strcmp("abcdff", "abef");//前面的字符串大于后面的返回>0(兩字符的差值)嗜侮,小于的返回小于0((兩字符的差值),帶符號)啥容,相等返回0锈颗;
//字符串拼接
strcat(str3, "f");
strcat(str3, "ferbe",1);//選一字節(jié)拼到str3
//將數(shù)字字符串轉(zhuǎn)為整數(shù)
int aa = atoi("255");//頭文件stdlib.h
int aa1 = atoi("gwg255");//返回0
int aa2 = atoi("255gree");//返回255
//整數(shù)轉(zhuǎn)字符串
char stra[20] = { 0 };
itoa(235, stra, 2);//將235以二進制存于stra
//sprintf將后面的值全轉(zhuǎn)為字符串存到stra里
sprintf(stra, "%d,%c,%f", 12, 'v', 12.3f);//不會輸出到控制臺,要用printf
//測試一些轉(zhuǎn)義字符的字節(jié)數(shù)
printf("%u\n", strlen("\n"));
// \123 \0123 數(shù)字都為八進制的咪惠、\xf45為16進制的
//字符串數(shù)組
char *sta1[3] = {"fwef","regfwef","tjyy"};
getchar();
return;
}
8.結(jié)構(gòu)體及數(shù)據(jù)存儲击吱。32位和4位cpu一次處理的數(shù)據(jù)分別為4字節(jié),8字節(jié)遥昧。數(shù)據(jù)存儲規(guī)則:字節(jié)對齊/內(nèi)存對齊覆醇。
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
//手動設(shè)置字節(jié)對齊,這里表示每次存四字節(jié)
#pragma pack(4)
//在全局區(qū)定義則全部區(qū)域可用
struct Stu
{
void(*p)(void);//結(jié)構(gòu)體不能定義函數(shù),若要傳入函數(shù)炭臭,就用指針
char name[10];
int age;
}stu1,stu2;//聲明變量方式1
struct
{
struct Stu stuu;//結(jié)構(gòu)體嵌套
char name[10];
int age;
}stu3={ "蔡徐坤",21 };//沒名字的要在下面聲明辦理永脓,不然其他地方難聲明。這里初始化是全局的
void fun(void){}
struct Stu1
{
char name[10];
int age;
};
int main(void)
{
struct Stu student = {"蔡徐坤",21};//聲明變量鞋仍,初始化(按順序)常摧。外面的變量也可這樣初始化,初始化就是全局的,這里的是局部的
//初始化指定元素struct Stu student = {.age=23 };
struct Stu *p = &student;
p->age;//結(jié)構(gòu)體指針調(diào)用方式
(&student)->name;//用點會報錯
strcpy((*p).name,"ysl");//設(shè)定定義字符串的值落午,不能直接用p->name="ysl"
strcpy(p->name, "ysl2");
student = (struct Stu) { "蔡徐坤2", 21 };//復合文字結(jié)構(gòu)
struct Stu student1 = {fun};//傳入函數(shù)的地址
//結(jié)構(gòu)體數(shù)組
struct Stu student3[3] = { { "蔡徐坤3", 21 },{ "蔡徐坤4", 21 },{ "蔡徐5", 21 } };
//結(jié)構(gòu)體大小也用sizeof(),計算規(guī)則依據(jù)于內(nèi)存對齊
struct Stu1 student11 = { "蔡徐坤啊",21 };//
printf("%u %u\n",sizeof(struct Stu1), sizeof(student11));
//(順序存儲谎懦,讀取順序)計算規(guī)則:1.以最大類型為字節(jié)對齊寬度2.一次填補各個成員字節(jié)3.結(jié)尾補齊。另外數(shù)組的話溃斋,就看初始定義的大小char a[10]党瓮,再根據(jù)基本類型中最大的(不是a[10])讀取字節(jié)數(shù)取算
system("pause");
return 0;
}
9.聯(lián)合體和枚舉
#include<stdio.h>
#include<stdlib.h>
//聯(lián)合類型,所有成員共用一塊內(nèi)存盐类。修改一個變量其他的也會被改變
union Un
{
char c;
short s;
int i;
}un1;
//枚舉 一組有名字的 int常數(shù)
enum Color {red ,black,white,blue=20};
int main(void)
{
printf("%p %p %p",&un1.c, &un1.s, &un1.i);//都一樣
union Un un2 = {.i=34};//只那能初始化一個
enum Color co = 202;//大小就是4字節(jié)
printf("%d %d %d %d %d",red,black,white,blue,co);// 0 1 2
system("pause");
return 0;
}