- 初始指針
使用指針來進行交換值
#include<stdio.h>
void change(int *a,int *b){
int tmp=*a;
*a = *b;
*b = tmp;
}
int main(){
int a=5,b=3;
change(&a,&b);
printf("num a = %d\nnum b = %d\n",a,b);
return 0;
}```
- gdb調試工具
`brew install gdb`
調試的時候,生成可以調試的版本,`gcc -g main.c -o main.out`
`gdb ./main.out`對程序進行調試,Mac里面必須是這樣`sudo gdb ./main.out`才能對程序進行調試。
`l`列出源代碼,按回車繼續剛才的命令
`break 12`斷點放在12行
`start`單步調試
`n`下一步
`p a`打印出a的變量值
`q`退出
`s`進入函數里面
`bt`查看函數堆棧
`f 1`跳到1這個棧里面
- 計算機中的數據表示方法
計算機中內存中最小單位是字節,1Byte字節=8bit位
- 內存管理
操作系統統一管理,32bit系統,最大使用4G內存,由尋址空間決定的,地址總線是32位,尋址空間是32位,也就是給內存編號只能有32位。
地址總線可以存在多種狀態,2^32個狀態,所以就是2^32字節,4GB,64位操作系統,64位的地址總線,所以內存足夠了,操作系統會給內存地址進行編號,編號就是唯一的內存字節的地址,一個字節存放8個二進制位的數據。
64位操作系統中,用戶應用程序使用的內存只要有前面的48位就夠了,其他的內存空間是給操作系統內核使用的
高位內存:系統內核->棧->堆->數據段->代碼段:低位內存
代碼編譯后的二進制數據加載到內存中,代碼段
聲明的全局變量和一些常量在數據段
中間的區域是自由可分配的內存
- 變量和指針的本質
靜態變量,靜態局部變量,作用域是當前函數,從定義位置,到其所在的{}結束位置,生命周期是從程序運行到程序退出,貫穿整個運行時間,下次函數調用的時候,靜態局部變量不會再次初始化,而是沿用上次函數退出時的值。
靜態全局變量,作用域為當前文件,從定義位置到文件結尾,聲明周期為從程序運行到程序退出,貫穿整個運行時間,但是動態全局變量的作用域是整個項目,即最終編譯成可執行文件的所有文件均可以使用動態全局變量。
C語言語法是不允許直接操作代碼段的,代碼段也是有地址的,除了代碼編譯后存在代碼段外,代碼的狀態信息是存在內存的棧區域。
C語言是強類型,靜態類型的。
變量的本質是一個代號,標識符,代表的是某個地址空間
指針本身也是一個變量,保存的數據是地址,指針變量的地址也就是指針的指針。指針的本質是保存內存地址。
- 操作系統對內存的管理
編譯器的優化功能,對源代碼進行優化,將同一類型的變量內存地址分配在一塊,使得程序執行更快。64位系統中指針是8個字節,因為地址總線是64位的。
棧的內存空間保存了程序運行時候的狀態。
代碼段的內存地址是向上增長分配的,然后棧段的內存地址是從大往小進行分配的。
- 函數棧以及數據段內存
棧里最先分配地址的函數內存地址更大一些,靜態變量,常量還有全局變量默認是在數據段中,靜態變量屬于某個函數特定的,一個函數被多次調用,但是都在數據段中。
- 函數指針與指針指向的數據訪問
`int (*pquadrate)(int a) = &quadrate;`定義一個函數指針
`int s = (*pquadrate)(a);`使用這個函數指針,()表示地址指向某個函數整體,然后后面調用傳參數a。如果指向棧內存,數據段內存表示直接去變量里面的值,指向代碼段,就是一片代碼塊。
- 示例代碼
include<stdio.h>
int global = 0;
int rect(int a,int b){
static int count = 0;
count++;
global++;
int s = a * b;
return s;
}
int quadrate(int a){
static int count = 0;
count++;
global++;
int s = rect(a,a);
return s;
}
int main(){
int a = 3;
int b = 4;
int *pa = &a;
int *pb = &b;
int pglobal = &global;
int (pquadrate)(int a) = &quadrate;
int s = quadrate(a);
printf("%d\n",s);
}```
- 數組申明的內存排列
x/3d 0x7fffffffde14
從這個地址開始顯示3個十進制,默認是按照4字節顯示
這里經過gcc優化,b的地址在最前面,其次是a,但是i的地址在$10 = (int *) 0x7fff5fbffc54
, b的地址$9 = (int *) 0x7fff5fbffc60
,a的地址$8 = (int *) 0x7fff5fbffc64
,array的地址$12 = (int (*)[3]) 0x7fff5fbffc6c
,說明這些變量之間的地址分配還不是連續的,但是數組間的地址分配是連續的。
#include<stdio.h>
int main(){
int a = 3;
int b = 2;
int array[3];//必須是常量
array[0] = 1;
array[1] = 10;
array[2] = 100;
int *p = &b;
int i;
for(i = 0;i < 6;i++){
printf("*p = %d\n",*p);
p++;
}
printf("------------------------\n");
p = &b;
for(i = 0;i<6;i++){
printf("p[%d]=%d\n",i,p[i]);
}
return 0;
}```
- 指針運算
`p++`定義了p是int類型,所以大小是4字節,自增就是到下一個4字節的地址。
`p[1]`表示把p向下偏移4個字節
指針是變量,數組是指針常量,數組本身也是地址。
- 字符數組和指針字符串
include<stdio.h>
int main(){
char str[] = "hello";
char *str2 = "world";
char str3[10];
printf("input the value\n");
scanf("%s",str3);
printf("str is %s\n",str);
printf("str2 is %s\n",str2);
printf("str3 is %s\n",str3);
}```
str2在代碼段,str2是指向這個地址而已,字符類型的指針,字符數組str3本身就是內存地址。str2指向字符串的指針,不能向里面scanf,str2是存的world字符串的地址,在代碼段的地址,不能被修改,而str是字符數組的地址,str地址是在棧里面,字符數組是以\0為結尾。x/6cb 0x7ffffffde00
打印6個字符,單字節顯示。
棧和堆內存才能被寫入。
- 字符數組
str[3]='\0'
表示字符串結束位置,str2指向的是常量字符串,所以不能被修改