gcc的簡(jiǎn)單使用
-
gcc -E hello.c -o hello.i
- 預(yù)處理.c文件女仰,處理文件包含猜年,宏定義,條件編譯
-
gcc -S hello.i -o hello.s
- 編譯董栽,將預(yù)處理過(guò)的文件編譯成匯編文件码倦。過(guò)程包括:詞法分析,語(yǔ)法分析锭碳,語(yǔ)意分析袁稽,代碼生成等。
-
gcc -c hello.s - o hello.o
- 匯編擒抛,生成目標(biāo)文件推汽,此時(shí)是二進(jìn)制代碼。目標(biāo)文件沒(méi)有鏈接動(dòng)態(tài)庫(kù)歧沪,還不是可執(zhí)行文件歹撒。
- 靜態(tài)庫(kù)生成(
ar -rc libxxx.a xxx1.o xxx2.o xxx3.o
),靜態(tài)庫(kù)以.a后綴結(jié)尾 - 動(dòng)態(tài)庫(kù)的生成(
gcc -fPIC -shared xxx1.c xxx2.c xxx3.c -o libxxx.so
)诊胞,動(dòng)態(tài)庫(kù)以.so后綴結(jié)尾
- 鏈接:目標(biāo)文件必須鏈接過(guò)后才可以執(zhí)行
- 靜態(tài)鏈接:
gcc -o hello hello.o libxxx.a
- 動(dòng)態(tài)鏈接:
gcc -o hello hello.o libxxx.so
-
lld.hello.exe
:在linux中可以通過(guò)shell腳本 lld 查看可執(zhí)行模塊的dependency
基本語(yǔ)法
- system函數(shù):
int system(const char * string);
- 在庫(kù)<stdlib.h>中暖夭,作用是開辟一個(gè)新的進(jìn)程執(zhí)行(調(diào)用
fork()
),來(lái)執(zhí)行參數(shù)字符串所代表的命令撵孤。
#include <stdio.h> #include <stdlib.h> // gcc 中有這個(gè)庫(kù) #include <windows.h> int main() { printf("before\n"); // 系統(tǒng)調(diào)用 system("dir"); // 調(diào)用外部程序 system("hello.exe"); printf("after\n"); // 使用 windows提供的庫(kù)函數(shù)迈着,調(diào)用windows操作系統(tǒng)的功能 WinExec("calc", SW_NORMAL); return 0; }
- 在庫(kù)<stdlib.h>中暖夭,作用是開辟一個(gè)新的進(jìn)程執(zhí)行(調(diào)用
- 宏
- 宏定義,會(huì)在預(yù)編譯時(shí)展開
// 定義一個(gè)宏 main.c #define NUM 100 int main(int argc, char const *argv[]) { int a; // 預(yù)處理會(huì)展開宏 a = NUM; return 0; } // 使用 `gcc -E` 預(yù)編譯后 main.i # 1 "main.c" # 1 "<built-in>" # 1 "<command-line>" # 1 "main.c" int main(int argc, char const *argv[]) { int a; 100 = 10; a = 100; return 0; }
- 進(jìn)制表示
0x:十六進(jìn)制邪码,0:八進(jìn)制
- 輸出時(shí)裕菠,
%d:有符號(hào)十進(jìn)制,%u無(wú)符號(hào)十進(jìn)制輸出闭专,%o:無(wú)符號(hào)八進(jìn)制奴潘,%x:無(wú)符號(hào)十六進(jìn)制
,輸出時(shí)影钉,以格式化輸出的類型來(lái)決定輸出的類型画髓,輸出時(shí)都是以四字節(jié)輸出。在以%u平委,%o雀扶,%x
輸出時(shí),如果原數(shù)據(jù)最高位是1,則高位補(bǔ)1愚墓。
char b = 0x81; printf("%x\n", b); // 輸出ffffff81
- 計(jì)算機(jī)在存儲(chǔ)時(shí)予权,都是以二進(jìn)制補(bǔ)碼形式存儲(chǔ),且每種類型的第一位為補(bǔ)碼浪册,注意在以
%d
輸出時(shí)的進(jìn)制轉(zhuǎn)換問(wèn)題扫腺。
char b = 0x81; printf("%x\n", b); //輸出 -127 // 1000 0001 char 類型是8位存儲(chǔ),第一位為符號(hào)位 // 原碼為 1111 1110 + 1 = 1111 1111 = -127
- 如果以小容量類型存儲(chǔ)大容量類型的數(shù)據(jù)村象,超出部分被舍棄笆环。以大容量數(shù)存儲(chǔ)小容量數(shù)據(jù),如果最高位是1厚者,則在前面全部補(bǔ)1補(bǔ)齊位數(shù)躁劣。
- 數(shù)據(jù)類型:每種數(shù)據(jù)類型都有一定的存儲(chǔ)大小,如果超過(guò)了存儲(chǔ)空間大小库菲,多出的位數(shù)則會(huì)舍棄账忘,所以在給一種類型賦值或計(jì)算時(shí),應(yīng)注意該數(shù)據(jù)類型的取值范圍熙宇,如果越界鳖擒,可能產(chǎn)生意料之外的結(jié)果。
- 整型
-
char烫止、unsigned char
:占一個(gè)字節(jié)蒋荚。'a'
為字符常量,打印格式為%c
馆蠕;"a"
為字符串(字符數(shù)組)期升,打印格式為%s
,每個(gè)字符串結(jié)尾互躬,編譯器都會(huì)自動(dòng)添加一個(gè)結(jié)束標(biāo)志位\0
播赁,兩者不一樣。-128 到 127 或 0 到 255吨铸。 -
short、unsigned short
類型祖秒,存儲(chǔ)整數(shù)诞吱,2字節(jié) -
int、unsigned int
類型竭缝,存儲(chǔ)整數(shù)房维,4字節(jié) -
long、unsigned long
抬纸,4字節(jié)
-
- 浮點(diǎn)型
-
float
咙俩,4字節(jié)耘擂,精度是六位小數(shù) -
double
蹬挺,8字節(jié),精度15位小數(shù) -
long double
,16字節(jié)颠悬,精度19位小數(shù)
-
- 數(shù)組
- 聲明
int arr[2] = {1, 2}
- 數(shù)組越界:編譯時(shí)不會(huì)出錯(cuò),但是當(dāng)程序運(yùn)行時(shí)拇派,如果用到了存儲(chǔ)越界數(shù)組項(xiàng)這塊空間時(shí)劈猪,會(huì)報(bào)錯(cuò)。
- 數(shù)組名指向數(shù)組的首地址命黔。c語(yǔ)言里面沒(méi)有獲取數(shù)組長(zhǎng)度的方法呜呐,
sizeof(arr) / sizeof(arr[0])
可以獲取數(shù)組長(zhǎng)度。 - 多維數(shù)組:
int arr[2][3] = { { 1, 2, 3 },{ 4, 5, 6 } }
- 字符數(shù)組和字符串:字符串以
\0 或 0
結(jié)尾悍募。字符串可以以%s
格式輸出蘑辑,原因是字符串輸出指針需要結(jié)束符0
。
#include <stdio.h> int main(int argc, char const *argv[]) { char ch[] = { 'h', 'e', 'l', 'l', 'o' }; // 字符數(shù)組坠宴,長(zhǎng)度是5 char str2 = "hello" // 這種方式初始化洋魂,默認(rèn)加 \0 結(jié)束符,所以長(zhǎng)度是6 char str[] = { 'h', 'e', 'l', 'l', 'o' '\0'}; // 字符串 char str1[] = { 'h', 'e', 'l', 'l', 'o' 0}; // 字符串 return 0; }
- 聲明
- 整型
- 流程控制s
- for(continue啄踊,break忧设,goto)
#include <stdio.h> int main(int argc, char const *argv[]) { goto next; // 跳轉(zhuǎn)到同一個(gè)作用域的標(biāo)記處 printf("zzzz1\n"); printf("zzzz2\n"); next: printf("123\n"); // 執(zhí)行這里 return 0; }
- while
- do...while
- 庫(kù)函數(shù)
- 隨機(jī)數(shù)
#include <stdio.h> #include <stdlib.h> #include <time.h> int main(int argc, char const *argv[]) { /* 1. srand函數(shù)是隨機(jī)數(shù)發(fā)生器的初始化函數(shù)。 2. 原型:void srand(unsigned int seed)颠通,如果傳入?yún)?shù)相同址晕,則產(chǎn)生隨機(jī)數(shù)也相同。 3. srand和rand()配合使用產(chǎn)生偽隨機(jī)數(shù)序列顿锰。 */ srand((unsigned int) time(NULL)); int i = 0; int num; for(i = 0; i < 5; i++) { // rand()函數(shù)返回一個(gè)隨機(jī)數(shù) num = rand(); printf("%d\n", num); } return 0; }
- 字符串處理函數(shù)
- 每個(gè)字符串常量都是一個(gè)地址谨垃,指向字符串的首元素地址。字符串存儲(chǔ)在
_data
的文字常量區(qū)硼控,相同的字符串擁有相同地址刘陶。 -
gets(char [])
:獲取一個(gè)鍵盤輸入的字符串,以回車結(jié)束牢撼,和scanf()
不同的是它可以讀取空格匙隔,這個(gè)函數(shù)不安全,已經(jīng)棄用熏版。 -
fgets(char *, size, *stream);
:可以指定讀取長(zhǎng)度纷责,換行符也會(huì)讀取。 -
strlen(s1)
:返回字符串s1的長(zhǎng)度撼短。和sizeof()
不同的是再膳,strlen()
不包括結(jié)束符\0
的長(zhǎng)度,而且遇到\0
結(jié)束曲横。 -
strcpy(dst, src)
:字符串拷貝喂柒,以首元素開始,遇到\0
結(jié)束。 -
strncpy(dst, src, length)
:字符串拷貝灾杰,可以拷貝\0蚊丐,但是不能拷貝\0后面的數(shù)據(jù)。 -
strcmp(s1, s2)
:如果 s1 和 s2 是相同的吭露,則返回 0吠撮;如果 s1<s2 則返回小于 0;如果 s1>s2 則返回大于 0讲竿。依次比較每個(gè)字符的ASCII碼泥兰。 -
strncmp(s1, s2, n)
:可以指定比較長(zhǎng)度n
。 -
strcat(s1, s2)
题禀,字符串拼接鞋诗。 -
sprintf(buf, "%d %d %d", a, b, c)
,將數(shù)據(jù)(a迈嘹,b削彬,c)以指定格式填充到buf
中。
#include <stdio.h> #include <string.h> int main(int argc, char const *argv[]) { int a, b , c; char buf[100]; a = 1, b = 2, c = 3; sprintf(buf, "%d %d %d", a, b, c); printf("buf = %s\n", buf); // buf = 1 2 3 return 0; }
-
sscanf(buf, "%d %d %d", a, b, c)
:將buf
中的數(shù)據(jù)以指定格式提取到(a秀仲,b融痛,c)中。注意格式要嚴(yán)格一致神僵。
#include <stdio.h> #include <string.h> int main(int argc, char const *argv[]) { int a, b , c; char buf[] = "a = 1, b = 2, c = 3"; sscanf(buf, "a = %d, b = %d, c = %d", &a, &b, &c); printf("a = %d b = %d c = %d\n", a,b,c); // a = 1 b = 2 c = 3 return 0; }
-
strchr(s1, ch)
:返回一個(gè)指針雁刷,指向字符串 s1 中字符 ch 的第一次出現(xiàn)的位置。 -
strstr(s1, s2)
:返回一個(gè)指針保礼,指向字符串 s1 中字符串 s2 的第一次出現(xiàn)的位置沛励。 -
char *strtok(char *str, const char *delim)
:該函數(shù)返回被分解的第一個(gè)子字符串,如果沒(méi)有可檢索的字符串炮障,則返回一個(gè)空指針目派,該函數(shù)會(huì)破壞原字符串,在匹配的位置加上\0
胁赢。第二次調(diào)用時(shí)企蹭,第一個(gè)參數(shù)寫NULL
=>char *strtok(NULL, const char *delim)
,切割的還是第一次調(diào)用被破壞的字符串智末。
- 每個(gè)字符串常量都是一個(gè)地址谨垃,指向字符串的首元素地址。字符串存儲(chǔ)在
- 函數(shù)
- 庫(kù)函數(shù)谅摄,使用頭文件引入,然后使用吹害。
- 自定義函數(shù)螟凭。
#include <stdio.h> // 無(wú)參 無(wú)返回值 void hello() { printf("hello func\n"); } // 含參無(wú)返回值 void add(int a, int b){ printf("%d\n", a + b); } // 含參有返回值 int multiply(int a, int b){ return a * b; } int main(int argc, char const *argv[]) { hello(); add(1, 2); printf("2 * 3 = %d\n", multiply(2, 3)); return 0; }
- 函數(shù)的聲明和定義: 在
main
函數(shù)中調(diào)用其他函數(shù)時(shí)虚青,只會(huì)往前去找函數(shù)的定義它呀,如果沒(méi)有定義就找函數(shù)的聲明, 如果聲明也沒(méi)有,c編譯器會(huì)報(bào)警告纵穿,c++會(huì)報(bào)錯(cuò)下隧。所以在調(diào)用函數(shù)之前,一定要進(jìn)行函數(shù)的聲明谓媒,聲明時(shí)形參名可以不寫淆院。
#include <stdio.h> // 這里是函數(shù)的聲明 int func(int a, int b); int main(int argc, char const *argv[]) { func(1, 2); return 0; } // 這里是函數(shù)的定義 int func(int a, int b) { return a + b; }
- 封裝庫(kù)函數(shù)。在
.c
文件中編寫自定義函數(shù)句惯,在.h
文件中寫函數(shù)聲明土辩,在用到這些函數(shù)時(shí),先將.h
文件包含進(jìn)來(lái)抢野,(用雙引號(hào)引入)拷淘,將這些c文件一起打包編譯。 -
.h
文件為了防止被多次包含指孤,可以在第一行加入启涯。
// 1. 條件編譯 #ifndef MY_STRLEN #define MY_STRLEN extern int my_strlen(char arr[]); #endif // 2. 或者在第一行加入 #pragma once
指針
- 指針是一個(gè)變量,其值為另一個(gè)變量的地址恃轩,即结洼,內(nèi)存位置的直接地址。就像其他變量或常量一樣叉跛,您必須在使用指針存儲(chǔ)其他變量地址之前,對(duì)其進(jìn)行聲明昧互。
- 指針也是一種數(shù)據(jù)類型挽铁。
- 指針指向誰(shuí),就把誰(shuí)的地址賦值給指針
- 直接操作指針變量本身沒(méi)有意義敞掘,需要操作
*p
---指針?biāo)赶虻膬?nèi)存叽掘。 - 指針的大小是由編譯器決定的。
#include <stdio.h> int main(int argc, char const *argv[]) { int *p; int a = 10; p = &a; printf("%p %p\n", p, &a); // 0061FF28 0061FF28 *p = 10; printf("%d %d\n", a, *p); // 10 10 return 0; }
- 野指針:指針保存了一個(gè)非法地址玖雁。(只有定義變量后系統(tǒng)分配的地址才是合法的)更扁,操作野指針時(shí)不會(huì)操作,但是在操作野指針指向的內(nèi)存時(shí)赫冬,由于系統(tǒng)在該進(jìn)程中未對(duì)改地址授權(quán)浓镜,會(huì)報(bào)一個(gè)段錯(cuò)誤。
- 空指針:給指針變量賦值為
NULL
劲厌。
int *p = NULL;
// 相當(dāng)于是
int *p;
p = NULL;
- 多級(jí)指針:指向指針的指針是一種多級(jí)間接尋址的形式膛薛,或者說(shuō)是一個(gè)指針鏈。通常补鼻,一個(gè)指針包含一個(gè)變量的地址哄啄。當(dāng)我們定義一個(gè)指向指針的指針時(shí)雅任,第一個(gè)指針包含了第二個(gè)指針的地址,第二個(gè)指針指向包含實(shí)際值的位置咨跌。
#include <stdio.h>
int main(int argc, char const *argv[])
{
int a = 10;
int *p = &a;
int **q = &p;
*p = 20;
**q = 30;
// a= 30 *p = 30 **q = 30
printf("a= %d *p = %d **q = %d \n ", a, *p, **q);
return 0;
}
- 指針操作方式
#include <stdio.h>
int main(int argc, char const *argv[])
{
int *p;
int a = 10;
p = &a;
// a = 10 *p = 10 *(p+0) = 10 p[0] = 10
printf("a = %d *p = %d *(p+0) = %d p[0] = %d\n", a, *p, *(p+0), p[0]);
return 0;
}
-
void
類型指針
#include <stdio.h>
int main(int argc, char const *argv[])
{
int a = 10;
/*
1. 不能用 void 定義變量沪么,因?yàn)闊o(wú)法確定變量要分配的內(nèi)存大小
2. 但是可以用 void 關(guān)鍵字定義指針,因?yàn)橹羔樀拇笮∈枪潭ǖ男堪耄鶕?jù)編譯器來(lái)確定禽车。
3. 在使用 void 關(guān)鍵字定義的指針是,需要進(jìn)行類型適配刊殉。
* 理由是地址指向的都是這個(gè)數(shù)據(jù)的首地址(int 4 字節(jié)的首地址)殉摔,操作整個(gè)4
字節(jié)的數(shù)據(jù),但是 void 類型沒(méi)有大小無(wú)法確定操作多大空間记焊。
*/
void *p;
p = &a;
//printf("*p = %d\n", *p); // error: invalid use of void expression
printf("*p = %d\n", *(int *)p); // 把 void 類型的指針轉(zhuǎn)換成 int 類型的指針
return 0;
}
- 指針的步長(zhǎng)钦勘,指針的加減法根據(jù)指針的步長(zhǎng)來(lái)進(jìn)行加減。指針的步長(zhǎng)由指針的類型決定亚亲,比如
int
類型的指針步長(zhǎng)是4彻采。
#include <stdio.h>
int main(int argc, char const *argv[])
{
int a = 10;
int *p = &a;
printf("p = %p P+1 = %p\n", p, p+1); // p = 0061FF28 P+1 = 0061FF2C
return 0;
}
- 指針與數(shù)組
- 數(shù)組存儲(chǔ)的是指針類型的數(shù)據(jù),每個(gè)數(shù)據(jù)是一個(gè)指針捌归。
int *p[3];
- 形參中的數(shù)組:形參中的數(shù)組不是數(shù)組肛响,是一個(gè)指針。
func(int a[100]){} // 形參大小是一個(gè)指針的大小 func(int a[]){} // 形參大小是一個(gè)指針的大小 func(int *a){} // 形參大小是一個(gè)指針的大小
- 數(shù)組名是一個(gè)指針惜索,指向數(shù)組第一個(gè)元素的地址特笋,可以利用指針步長(zhǎng)的特性來(lái)遍歷數(shù)組。
- 數(shù)組存儲(chǔ)的是指針類型的數(shù)據(jù),每個(gè)數(shù)據(jù)是一個(gè)指針捌归。
- 指針作為函數(shù)返回值:注意巾兆,不能返回一個(gè)局部變量的地址猎物,局部變量在函數(shù)調(diào)用完成時(shí)會(huì)自動(dòng)銷毀。如果再用一個(gè)指針去接收這個(gè)地址會(huì)產(chǎn)生一個(gè)野指針角塑。
#include <stdio.h>
int a; // 全局變量 在 bss區(qū)(未初始化全局變量)
int *func(){
return &a;
}
int main(int argc, char const *argv[])
{
*func() = 100;
printf("a = %d\n", a);
return 0;
}
作用域
- 局部作用域
- 在
{}
內(nèi)定義的變量只能在該局部作用域內(nèi)使用蔫磨,在代碼執(zhí)行到這個(gè)變量的定義時(shí),編譯器才會(huì)為這個(gè)變量分配空間圃伶。 - 在不同的作用域中可以定義同名的變量堤如。
#include <stdio.h> int main(int argc, char const *argv[]) { int a = 1; if(1) { int a = 2; printf("%d\n", a); // 2 } return 0; }
-
static
關(guān)鍵字聲明的靜態(tài)局部變量在程序編譯的時(shí)候就會(huì)分配地址空間(_data區(qū)),不能用局部變量初始化窒朋,靜態(tài)局部變量在程序退出時(shí)銷毀搀罢。static
初始化的局部變量只會(huì)初始化一次,但是可以賦值多次侥猩。
#include <stdio.h> void func(){ static int a = 1; a++; printf("%d\n", a); } int main(int argc, char const *argv[]) { func(); // 2 func(); // 3 func(); // 4 return 0; }
- 在
- 全局作用域
- 全局變量榔至,在編譯時(shí)分配地址空間,如果沒(méi)有初始化欺劳,默認(rèn)值為0唧取,在程序(進(jìn)程)結(jié)束時(shí)銷毀瓣俯,提前使用(定義前使用)需要先聲明,可以聲明多次兵怯。
#include <stdio.h> void func(){ extern int a; // 在這之前未定義,需要聲明 printf("func = %d\n", a); } int a = 10; void func1(){ printf("func1 = %d\n", a); } void func2(){ int a = 20; printf("func2 = %d\n", a); } int main(int argc, char const *argv[]) { func(); func1(); func2(); return 0; }
-
static
全局變量(包括函數(shù))腔剂,和普通全局變量的區(qū)別就是作用域不同媒区,只在本文件內(nèi)可用。
內(nèi)存分配
-
在程序執(zhí)行前掸犬,有幾個(gè)分區(qū)的內(nèi)存已經(jīng)確定袜漩,可以在
linux
使用size
命令查看。- text(代碼區(qū)):一般只讀湾碎,函數(shù)宙攻。
- data:已初始化的數(shù)據(jù),全局變量介褥,
static
變量座掘,文字常量區(qū)(只讀)。 - bss:未初始化數(shù)據(jù)柔滔,全局變量溢陪,
static
變量
-
在程序運(yùn)行時(shí)除了加載上述已確定的內(nèi)存外,還加載包括堆區(qū)內(nèi)存和棧區(qū)內(nèi)存睛廊。
-
棧內(nèi)存(stack):存放普通局部變量形真,自動(dòng)管理內(nèi)存。
- 堆內(nèi)存(heap):手動(dòng)申請(qǐng)空間咆霜,手動(dòng)釋放或程序結(jié)束系統(tǒng)釋放,使用函數(shù)
malloc(int size)
申請(qǐng)堆內(nèi)存嘶朱。
#include <stdio.h> #include <stdlib.h> int main(int argc, char const *argv[]) { int *p; p = (int *)malloc(sizeof(int)); if(NULL == p){ return -1; // 空間分配失敗 } *p = 100; printf("%d\n", *p); // 100 printf("%p\n", p); // 00FF2A48 if(NULL != p){ // 釋放 p 所指向的內(nèi)存蛾坯, p 中存儲(chǔ)的地址信息不變,這個(gè)地址不能再被使用疏遏。 free(p); p = NULL; } //*p = 20; // err return 0; } }
-
結(jié)構(gòu)體
- 定義和聲明
#include <stdio.h>
#include <string.h>
struct Student
{
char name[50];
int age;
};
struct Teacher // 定義并聲明
{
char name[50];
int age;
}t1, t2 = { "hgz",18 };
struct // 定義匿名結(jié)構(gòu)體并聲明
{
char name[50];
int age;
}w1 = { "hgz",18 }, w2;
int main(int argc, char const *argv[])
{
struct Student stu1 = { "hgz",18 };
struct Student stu2;
strcpy(stu2.name, "hgz");
stu2.age = 18;
struct Student stu3;
struct Student *p;
p = &stu3;
strcpy(p->name, "hgz");
p->age = 18;
printf("%s\n", "ok");
return 0;
}
- 結(jié)構(gòu)體數(shù)組
struct Student stu[2] = {
{"hgz", 18},
{"zzz", 19}
};
- 結(jié)構(gòu)體直接作為參數(shù)傳遞是值傳遞偿衰,如果需要修改原結(jié)構(gòu)體,需要用地址傳遞(參數(shù)為結(jié)構(gòu)體指針)改览。
#include <stdio.h>
struct Student
{
int age;
};
void func(struct Student *p){
p->age = 20;
}
int main(int argc, char const *argv[])
{
struct Student s1 = {18};
func(&s1);
printf("%d\n", s1.age);
return 0;
}
- 結(jié)構(gòu)體中的指針下翎。(要先給地址再使用,可以給文字常量區(qū)字符串地址宝当,棧地址或者堆區(qū)地址)
#include <stdio.h>
#include <string.h>
struct Student
{
char *name;
int age;
};
int main(int argc, char const *argv[])
{
// 相當(dāng)于 s1.name = "zzz"; 將data區(qū)文字常量的字符串的首地址賦值給指針
struct Student s1 = {"zzz", 18};
printf("s1 = %s %d\n", s1.name, s1.age);
struct Student s2;
// strcpy(s2.name, "hgz"); // err 野指針 s2.name 還沒(méi)有地址
char name[100];
s2.name = name; // 分配棧區(qū)空間
strcpy(s2.name, "hgz");
s2.age = 20;
printf("s2 = %s %d\n", s2.name, s2.age);
struct Student s3;
s3.name = (char *)malloc(strlen("hgzzz")); // 分配堆區(qū)空間
strcpy(s3.name, "hgzzz");
s3.age = 22;
printf("s3 = %s %d\n", s3.name, s3.age);
if(NULL != s3.name){ // 注意使用堆區(qū)地址時(shí)视事,使用完要釋放
free(s3.name);
s3.name = NULL;
}
return 0;
}
共用體
- 共用體所有成員公用一塊內(nèi)存地址,內(nèi)存大小為最大成員內(nèi)存大小庆揩。所有成員都指向首地址俐东。
#include <stdio.h>
union Test{
unsigned int a;
unsigned short b;
unsigned char c;
};
int main(int argc, char const *argv[])
{
union Test t1;
// &t1.a = 0061FF2C &t1.b = 0061FF2C &t1.c = 0061FF2C 共用體成員指向同一塊地址頭
printf("&t1.a = %p &t1.b = %p &t1.c = %p\n", &t1.a, &t1.b, &t1.c);
t1.a = 0x44332211;
// t1.a = 44332211 t1.b = 2211 t1.c = 11 共用體公用一塊內(nèi)存
printf("t1.a = %x t1.b = %x t1.c = %x\n", t1.a, t1.b, t1.c);
t1.c = 0xaa;
// t1.a = 443322aa t1.b = 22aa t1.c = aa 修改共用體中的成員可能會(huì)影響其他成員
printf("t1.a = %x t1.b = %x t1.c = %x\n", t1.a, t1.b, t1.c);
return 0;
}
枚舉類型
- 定義
#include <stdio.h>
enum{ // 不初始化賦值默認(rèn)為 0 開始遞增
pink, red, yellow
};
int main(int argc, char const *argv[])
{
printf("%d\n", pink == 0 ); // 1
printf("%d\n", red == 1 ); // 1
printf("%d\n", yellow == 2 ); // 1
return 0;
}
-
typedef
關(guān)鍵字:給一個(gè)已存在的類型取一個(gè)別名跌穗。
#include <stdio.h>
int main(int argc, char const *argv[])
{
typedef int int32; // 當(dāng)前使用的是32位gcc編譯器
typedef struct Student
{
int32 age; // typedef替換發(fā)生在編譯階段 宏定義是在預(yù)編譯階段
}Student;
Student s1 = {18};
printf("%d\n", s1.age);
return 0;
}
文件操作
-
FILE
結(jié)構(gòu)體:我們?cè)谶M(jìn)行文件操作時(shí)要用到這個(gè)結(jié)構(gòu)體,在我們調(diào)用fopen(const char * filename, const char * mode)
時(shí)虏辫,會(huì)返回一個(gè)指向堆區(qū)的指針(這個(gè)指針指向結(jié)構(gòu)體的地址蚌吸,和文件沒(méi)有直接關(guān)系)。這個(gè)函數(shù)會(huì)初始化結(jié)構(gòu)體成員砌庄,這些成員和文件有著聯(lián)系羹唠,當(dāng)我們?cè)谶M(jìn)行文件操作時(shí),實(shí)際上是這些成員在操作娄昆。
typedef struct{
short level; // 緩存區(qū)‘滿’或者‘空’的程度
unsigned flags; // 文件狀態(tài)標(biāo)志
char fd; // 文件描述
unsigned char hold; // 如無(wú)緩沖區(qū)不讀取字符
short bsize; // 緩沖區(qū)大小
unsigned char *buffer; // 數(shù)據(jù)緩沖區(qū)位置
unsigned ar; // 指針佩微,在文件中的指向位置
unsigned istemp; // 臨時(shí)文件指示器
short token; // 用于有效性檢查
}FILE;
- 使用方式
#include <stdio.h>
int main(int argc, char const *argv[])
{
// 第二個(gè)參數(shù)為 w 表示以寫操作打開文件,如果文件不存在萌焰,創(chuàng)建文件哺眯,
// 如果存在,刪除內(nèi)容并打開
FILE *fp = NULL;
fp = fopen("b.txt", "w");
if(NULL == fp){
perror("fopen");
return -1;
}
// fputs(int a, FILE *stream)
char a = 'a';
while(a <= 'z'){
fputc(a, fp);
a++;
}
// 關(guān)閉文件
fclose(fp);
fp = NULL;
return 0;
}
- 標(biāo)準(zhǔn)文件輸出
#include <stdio.h>
int main(int argc, char const *argv[])
{
printf("%s\n", "zzzz"); // 標(biāo)準(zhǔn)輸出(屏幕輸出) zzzz
fclose(stdout); // 關(guān)閉標(biāo)準(zhǔn)輸出文件指針
printf("%s\n", "hgz"); // 無(wú)輸出
perror("abc"); // 打印函數(shù)調(diào)用失敗的原因 abc : Bad file descriptor
return 0;
}
- 按塊大小讀寫文件
fread() fwrite()
#include <stdio.h>
typedef struct Student
{
char name[50];
int age;
}Student;
int main(int argc, char const *argv[])
{
Student stus[4] = {
{ "hgz1", 18 },
{ "hgz2", 19 },
{ "hgz3", 20 },
{ "hgz4", 21 }
};
FILE *fp = fopen("6.txt", "w");
if(NULL == fp){
perror("fopen");
return -1;
}
int res = fwrite(stus, sizeof(stus), 1, fp);
printf("%d\n", res);
fclose(fp);
fp = NULL;
return 0;
}
#include <stdio.h>
typedef struct Student
{
char name[50];
int age;
}Student;
int main(int argc, char const *argv[])
{
FILE *fp = fopen("6.txt", "r");
Student stus[10];
int res = fread(stus, sizeof(Student), 4, fp);
printf("res = %d\n", res);
for(int i = 0; i < 4; i++){
printf("%s %d\n", stus[i].name, stus[i].age);
}
fclose(fp);
return 0;
}
用fread() fwrite()
實(shí)現(xiàn)文件拷貝命令
#include <stdio.h>
int main(int argc, char const *argv[])
{
// 模擬linux cp 命令 -> cp a.txt b.txt 將a.txt 內(nèi)容復(fù)制到b.txt中
if(argc != 3){
printf("%d\n", argc);
printf("argument count error:a.exe src dst\n");
return 0;
}
// 如果第二個(gè)參數(shù)是 r 在windows處理二進(jìn)制文件會(huì)出問(wèn)題
FILE *srcfp = fopen(argv[1], "rb"); // 源文件
FILE *dstfp = fopen(argv[2], "wb"); // 拷貝目的文件
char buf[4*1024];
int len; // 每次讀取的長(zhǎng)度
while(1){
len = fread(buf, 1, sizeof(buf), srcfp);
printf("len = %d\n", len);
if(len == 0){
break;
}
fwrite(buf, 1, len, dstfp);
};
fclose(dstfp);
fclose(srcfp);
return 0;
}
- 文件讀寫緩存區(qū)扒俯,在讀寫文件時(shí)奶卓,先把數(shù)據(jù)讀寫到緩沖區(qū)中。當(dāng)緩存區(qū)滿撼玄、調(diào)用(fclose()寝杖、fflush())等刷新緩存區(qū)函數(shù)、程序關(guān)閉時(shí)才從緩沖區(qū)讀寫文件互纯。