自從出來工作之后就沒在用過 c 了,為了更加深入的學(xué)習(xí)一些底層的東西症副,不得已重新來過一遍基礎(chǔ)的知識(shí)點(diǎn)启泣。
C的基本數(shù)據(jù)類型
signed&unsigned
- int是4個(gè)字節(jié),32位
- int能表示2的32次方個(gè)數(shù)字
- 把最高位抽出來亿蒸,用于表示正負(fù)凑兰,只剩下31位表示數(shù)值
- 此時(shí)int能表示2的31次方
- 如果最高位的值是0掌桩,那么int就是正數(shù),范圍就是0~2的31次方-1
- 如果最高位的值是1姑食,那么int就是負(fù)數(shù)波岛,范圍就是-(2的31次方)~-1
- 那么int的總范圍,就是-2的31次方~2的31次方-1
- signed:有符號(hào)音半,表示最高位用于表示正負(fù)
- unsigned:無符號(hào)则拷,表示最高位用于表示數(shù)值
- 無符號(hào)int型只能表示正數(shù),并且取值范圍是0~2的32次方-1
原碼曹鸠、反碼煌茬、補(bǔ)碼
- 原碼:把數(shù)值轉(zhuǎn)換成二進(jìn)制數(shù)值
- 反碼:除最高位,其他位依次取反
- 補(bǔ)碼:在補(bǔ)碼的基礎(chǔ)上物延,除最高位宣旱,其他位依次取反,然后+1
- 正數(shù)的原碼叛薯、反碼浑吟、補(bǔ)碼相同
基本數(shù)據(jù)類型的長(zhǎng)度
JAVA
- byte:1
- short:2
- int:4
- long:8
- float:4
- double:8
- boolean:1
- char:2
C
- short:2
- int:4
- long:32位操作系統(tǒng)是4個(gè)字節(jié),64位操作系統(tǒng)是8字節(jié)
- float:4
- double:8
- char:1
示例代碼
#include <stdio.h>
int main(int argc, const char * argv[]) {
//基本數(shù)據(jù)類型的長(zhǎng)度
//sizeof返回的 unsign long 無符號(hào)的long類型
printf("%lu\n",sizeof(int));//4
printf("%lu\n", sizeof(short));//2
printf("%lu\n", sizeof(long));//在32位操作系統(tǒng)中l(wèi)ong的長(zhǎng)度4個(gè)字節(jié)耗溜,64位操作系統(tǒng)中l(wèi)ong的長(zhǎng)度為8個(gè)字節(jié)
printf("%lu\n", sizeof(float));//4
printf("%lu\n", sizeof(double));//8
printf("%lu\n", sizeof(char));//1
printf("%lu\n", sizeof(long long));//8
//以下兩種聲明的方式组力,我覺得是沒有必要的,因?yàn)橐硎?個(gè)長(zhǎng)度的數(shù)據(jù)類型抖拴,直接使用 short 就好的燎字,要表8位長(zhǎng)度的數(shù)據(jù)使用 long 就行了。
printf("%lu\n",sizeof(short int));//2
printf("%lu\n",sizeof(long int));//8
//定義一個(gè)無符號(hào)的變量a阿宅,最大值42億
unsigned int a = 4294967295;
//最高位表示符號(hào)位候衍,只能表示無符號(hào)的一半大小,默認(rèn)定義的整形類型就是有符號(hào)位的
signed int b = 2100000000;
//如果使用無符號(hào)來表示-1的話洒放,它表示的是無符號(hào)的最大值4294967295蛉鹿。
unsigned int c = -1;
printf("%u\n",a);
printf("%d\n",b);
printf("%u\n",c);
//char只能存放一個(gè)字符,并且使用''表示
char ch = 'b';
printf("%c",ch);
return 0;
}
指針
內(nèi)存地址
- 內(nèi)存中每一個(gè)字節(jié)都有一個(gè)內(nèi)存地址往湿,這樣才能保證不管數(shù)據(jù)存在內(nèi)存的哪一個(gè)位置妖异,都可以通過內(nèi)存地址找到。
- 訪問內(nèi)存的數(shù)據(jù)领追,要通過內(nèi)存地址他膳。
指針變量
- 指針變量就是保存內(nèi)存地址的變量。
- 指針變量通常簡(jiǎn)稱為指針绒窑。
- int* p;定義一個(gè)int型的指針變量棕孙,用于保存內(nèi)存地址,該地址存放的值必須是int型的數(shù)據(jù)。
- *p 獲取指針變量 p 指向的內(nèi)存地址所存放的數(shù)據(jù)蟀俊。
#include<stdio.h>
#include<stdlib.h>
int main(){
int i;
i = 10;
//打印變量的地址分歇,注意占位符使用%#x
//printf("%#x\n",&i);
int b = 3;
//定義一個(gè)int類型的指針,存放是int類型變量的地址欧漱。
//這是一個(gè)一級(jí)指針
int *p = &b;
//定義一個(gè)二級(jí)指針,存放的是指針的地址葬燎。
int **q = &p;
printf("p指針?biāo)娣艃?nèi)存地址對(duì)應(yīng)的值是%d\n",*p);
printf("q指針?biāo)娣艃?nèi)存地址對(duì)應(yīng)的值是%d\n",**q);
//指針的常見錯(cuò)誤
//常見錯(cuò)誤1:野指針
int *pp ;
//定義了误甚,但沒有賦值的指針,稱為野指針谱净。
//printf("pp的內(nèi)存地址是%#x\n",&pp);
int a = 3;
//在這里直接將a變量賦值給一個(gè)指針變量的指向的內(nèi)存單元窑邦。這是不行的,因?yàn)檫@是野指針壕探。
//*pp = a;
//正確做法應(yīng)該是:給指針pp賦值冈钦,讓其指向一個(gè)有效的內(nèi)存地址。
pp = &a;
printf("%d\n",*pp);
//常見錯(cuò)誤2:指針類型錯(cuò)誤
double d = 3.14;
//給指針變量賦值錯(cuò)了數(shù)據(jù)類型的地址李请。
//pp = &d;
return 0;
}
指針的長(zhǎng)度
在 32 位操作系統(tǒng)中瞧筛,指針的長(zhǎng)度是 4 個(gè)字節(jié),在 64 位操作系統(tǒng)中导盅,指針的長(zhǎng)度是 8 位较幌。
-
那為什么還要給指針分不同的類型呢?
因?yàn)樵谧鲋羔樜灰茣r(shí)白翻,需要計(jì)算指針位移運(yùn)算乍炉。
多級(jí)指針&多級(jí)指針的訪問
引用傳遞
#include<stdio.h>
//引用傳遞
void swap(int *a ,int *b){
int temp = *a;
*a = *b;
*b = temp;
}
int main() {
int a = 10;
int b = 20;
swap(&a,&b);
printf("a = %d\n",a);
printf("b = %d\n",b);
return 0;
}
主函數(shù)獲取子函數(shù)的變量的內(nèi)存地址
#include<stdio.h>
//錯(cuò)誤版本
//void function(int* p){
//
// int i = 3;
// p = &i;
// printf("i 的地址是%p\n",&i);
//}
//i 的地址是0x7fff4fda8994
//mainp指針存放的地址是:0x7fff4fda89e8
//int main() {
//
//
// //定義一個(gè)可以接收地址的指針
// int* mainp;
//
// function(mainp);
//
// printf("mainp指針存放的地址是:%p\n",mainp);
// return 0 ;
//}
//正確版本
void function(int** p){
int i = 3;
*p = &i;
printf("i 的地址是%p\n",&i);
}
//i 的地址是0x7fff5607a994
//mainp指針存放的地址是:0x7fff5607a994
int main() {
//定義一個(gè)可以接收地址的指針
int* mainp;
function(&mainp);
printf("mainp指針存放的地址是:%p\n",mainp);
return 0 ;
}
常見錯(cuò)誤
- 指針沒賦值就使用,沒賦值的指針叫野指針
- 指針類型弄錯(cuò)滤馍,給int型指針指定了一個(gè)非int型數(shù)據(jù)的地址
數(shù)組
- C語言的數(shù)組不檢測(cè)越界
- 數(shù)組的內(nèi)存地址是連續(xù)的空間
- 數(shù)組變量名保存的地址就是數(shù)組第0個(gè)元素的地址
- 內(nèi)存地址可以加減運(yùn)算
- 內(nèi)存地址+1表示右移一個(gè)單位
- 指針的長(zhǎng)度就是4個(gè)字節(jié)
- 不同的指針類型的長(zhǎng)度都是一樣的岛琼,分不同類型的指針是在位移時(shí)比較方便。
#include <stdio.h>
int main(){
char c[] = "hello";
printf("第8個(gè)元素的值是%c\n",c[8]);//c語言的數(shù)組是不檢測(cè)越界巢株。
//數(shù)組每一個(gè)元素的地址都是連續(xù)的槐瑞。
printf("c[0]的地址%#x\n",&c[0]);//c[0]的地址0xeef9f98a
printf("c[1]的地址%#x\n",&c[1]);//c[1]的地址0xeef9f98b
printf("c[2]的地址%#x\n",&c[2]);//c[2]的地址0xeef9f98c
printf("c[3]的地址%#x\n",&c[3]);//c[3]的地址0xeef9f98d
printf("c[4]的地址%#x\n",&c[4]);//c[4]的地址0xeef9f98e
//打印數(shù)組變量名字的地址
printf("c數(shù)組變量名字的地址是%#x\n",&c);//c數(shù)組變量名字的地址是0xeef9f98a
int data[] = {1,2,3,4,5};
//數(shù)組每一個(gè)元素的地址都是連續(xù)的。整型數(shù)據(jù)每一個(gè)元素之間相差4個(gè)字節(jié)
printf("data[0]的地址%#x\n",&data[0]);//data[0]的地址0xe538f970
printf("data[1]的地址%#x\n",&data[1]);//data[1]的地址0xe538f974
printf("data[2]的地址%#x\n",&data[2]);//data[2]的地址0xe538f978
printf("data[3]的地址%#x\n",&data[3]);//data[3]的地址0xe538f97c
printf("data[4]的地址%#x\n",&data[4]);//data[4]的地址0xe538f980
//打印數(shù)組變量名字的地址
printf("data數(shù)組變量名字的地址是%#x\n",&data);//data數(shù)組變量名字的地址是0xe538f970
//打印的數(shù)組變量和打印數(shù)組變量的地址是一樣的纯续,都是表示數(shù)組第一個(gè)元素的地址随珠。
printf("data數(shù)組變量是%#x\n",data);//data數(shù)組變量名字的地址是0xe538f970
//數(shù)組變量名字的地址就是數(shù)組第一個(gè)元素的地址
//內(nèi)存地址是可以進(jìn)行加減運(yùn)算的。
//定義一個(gè) cp 指針猬错,存放數(shù)組cy首元素的地址
//指針+1表示內(nèi)存地址位移一位
char* cp = &c;
printf("第1個(gè)元素的值%c\n",*(cp+0));//h
printf("第2個(gè)元素的值%c\n",*(cp+1));//e
printf("第3個(gè)元素的值%c\n",*(cp+2));//l
printf("第4個(gè)元素的值%c\n",*(cp+3));//l
printf("第5個(gè)元素的值%c\n",*(cp+4));//o
int* datap = &data;
printf("第1個(gè)元素的值%d\n",*(datap+0));//1
printf("第2個(gè)元素的值%d\n",*(datap+1));//2
printf("第3個(gè)元素的值%d\n",*(datap+2));//3
printf("第4個(gè)元素的值%d\n",*(datap+3));//4
printf("第5個(gè)元素的值%d\n",*(datap+4));//5
}
數(shù)組與指針的關(guān)系
如何用指針表示數(shù)組
int a[] = {1,2,3,4};
//使用指針p存放數(shù)組a首個(gè)元素的地址窗看。
int *p = a;
如何通過指針訪問數(shù)組元素
//使用指針修改數(shù)組的值
*(p+0) = 10;
示例代碼
#include<stdio.h>
int main() {
int a[] = {1,2,3,4};
//使用指針p存放數(shù)組a首個(gè)元素的地址。
int *p = a;
printf("數(shù)組變量:%p\n",a);//0x7ffeede7d960
printf("指針地址:%p\n",p);//0x7ffeede7d960
//使用指針修改數(shù)組的值
*(p+0) = 10;
printf("指針訪問數(shù)組元素:%d\n",*(p+0));
return 0;
}
常見錯(cuò)誤
- 指針沒賦值就使用倦炒,沒賦值的指針叫野指針
- 指針類型弄錯(cuò)显沈,給int型指針指定了一個(gè)非int型數(shù)據(jù)的地址
#include<stdio.h>
int main() {
int * p;
int a = 1;
double d = 4.0;
//printf("%p\n",&p);//指針沒有指向不能輕易使用,它是一個(gè)野指針
p = &a;
printf("*p = %d\n",*p);//*p = 1
//指針指向地址存放的類型必須和聲明指針的類型一致
//p = & d;
return 0;
}
堆與棧
棧
- 棧是自動(dòng)分配,自動(dòng)釋放
- 大小固定
- 從棧分配的內(nèi)存空間叫靜態(tài)內(nèi)存
堆
- malloc或者new申請(qǐng)
- c語言需要用free釋放
- 從堆分配內(nèi)存空間叫動(dòng)態(tài)內(nèi)存
- realloc 是在原來的基礎(chǔ)上再分配內(nèi)存空間
malloc
- 動(dòng)態(tài)申請(qǐng)內(nèi)存空間
- 一個(gè)malloc申請(qǐng)出來的內(nèi)存地址是連續(xù)的
- malloc(需要申請(qǐng)的字節(jié)數(shù)) 返回一個(gè)指針
malloc 的圖解
示例代碼
#include <stdio.h>
#include <stdlib.h>
int main() {
int len ;
printf("請(qǐng)輸入數(shù)組的長(zhǎng)度\n");
scanf("%d",&len);
//malloc動(dòng)態(tài)分配內(nèi)存拉讯,p指針指向首字節(jié)地址
int *p = (int*)malloc(sizeof(int)*len);
for(int i = 0;i<len;i++){
*(p+i) = i*10;
}
for(int i = 0;i<len;i++){
printf("第%d個(gè)數(shù)據(jù)是%d\n",(i+1),p[i]);
}
printf("p指針變量的地址:%p\n",p);
printf("第1個(gè)元素的值:%d\n",*(p));
int a = p[1];
printf("malloc 分配的內(nèi)存空間指針相當(dāng)于一個(gè)數(shù)組涤浇,%d\n",a);
//釋放在堆中動(dòng)態(tài)分配的內(nèi)存空間;
//free()釋放的是指針指向的內(nèi)存魔慷。指針變量依然存在只锭,只有程序結(jié)束時(shí)才被銷毀。不過現(xiàn)在指針指向的內(nèi)容是未定義的垃圾院尔,所以現(xiàn)在指針又成為了野指針蜻展。
//void free(void *FirstByte):該函數(shù)是將之前用malloc分配的空間還給程序或者是操作系統(tǒng),也就是釋放了這塊內(nèi)存邀摆,讓它重新得到自由纵顾。
free(p);
printf("p指針變量的地址:%p\n",p);
printf("第1個(gè)元素的值:%d\n",*(p));
for(int i = 0;i<len;i++){
printf("第%d個(gè)數(shù)據(jù)是%d\n",(i+1),p[i]);
}
/**
在 Mac OS 系統(tǒng):
free(p) 之后 *P 沒有變化,p的地址沒有發(fā)生變化
在 Linux 系統(tǒng):
free(p) 之后 *p 的值不再是 free 之前的值
*/
return 0;
}
realloc
重新分配主存
示例代碼
#include <stdio.h>
#include <stdlib.h>
int main() {
int len ;
printf("請(qǐng)輸入數(shù)組的長(zhǎng)度\n");
scanf("%d",&len);
//malloc動(dòng)態(tài)分配內(nèi)存栋盹,p指針指向首字節(jié)地址
int *p = (int*)malloc(sizeof(int)*len);
for(int i = 0;i<len;i++){
*(p+i) = (i+1)*10;
}
for(int i = 0;i<len;i++){
printf("第%d個(gè)數(shù)據(jù)是%d\n",(i+1),p[i]);
}
for(int i = 0;i<len;i++){
printf("指針p[%d]的地址是:%p\n",(i+1),(p+i));
}
int size = 0;
printf("請(qǐng)輸入需要補(bǔ)充的值\n");
scanf("%d",&size);
p = (int *)realloc(p,size+len);
for(int i = 0;i<len+size;i++){
*(p+i) = (i+1)*10;
}
for(int i = 0;i<len+size;i++){
printf("第%d個(gè)數(shù)據(jù)是%d\n",(i+1),p[i]);
}
for(int i = 0;i<len+size;i++){
printf("指針p[%d]的地址是:%p\n",(i+1),(p+i));
}
free(p);
return 0;
}
/*
realloc函數(shù)的使用
函數(shù)名: realloc
功 能: 重新分配主存
用 法: void *realloc(void *ptr, unsigned newsize);
在指針 ptr 后面分配新的內(nèi)存空間施逾。
請(qǐng)輸入數(shù)組的長(zhǎng)度
4
第1個(gè)數(shù)據(jù)是10
第2個(gè)數(shù)據(jù)是20
第3個(gè)數(shù)據(jù)是30
第4個(gè)數(shù)據(jù)是40
指針p[1]的地址是:0x7fd4e7402ab0
指針p[2]的地址是:0x7fd4e7402ab4
指針p[3]的地址是:0x7fd4e7402ab8
指針p[4]的地址是:0x7fd4e7402abc
請(qǐng)輸入需要補(bǔ)充的值
3
第1個(gè)數(shù)據(jù)是10
第2個(gè)數(shù)據(jù)是20
第3個(gè)數(shù)據(jù)是30
第4個(gè)數(shù)據(jù)是40
第5個(gè)數(shù)據(jù)是50
第6個(gè)數(shù)據(jù)是60
第7個(gè)數(shù)據(jù)是70
指針p[1]的地址是:0x7fd4e7402ab0
指針p[2]的地址是:0x7fd4e7402ab4
指針p[3]的地址是:0x7fd4e7402ab8
指針p[4]的地址是:0x7fd4e7402abc
指針p[5]的地址是:0x7fd4e7402ac0
指針p[6]的地址是:0x7fd4e7402ac4
指針p[7]的地址是:0x7fd4e7402ac8
*/
自定義類型
結(jié)構(gòu)體
- 結(jié)構(gòu)體的定義
//1.定義一個(gè)結(jié)構(gòu)體
struct Student {
int age;
char *name;
char sex;
//eatp表示函數(shù)指針的名字
void(*eatp)();
};
- 結(jié)構(gòu)體成員的賦值
//方式1
struct Student st = {
11,"張三",'m'
};
//方式2
//給結(jié)構(gòu)體內(nèi)的成員賦值
st.age = 12;
st.name = "李四";
st.sex = 'm';
- 結(jié)構(gòu)體函數(shù)指針
結(jié)構(gòu)體內(nèi)指針不能定義函數(shù),只能定義函數(shù)指針例获。
//給結(jié)構(gòu)體指針賦值
st.eatp = eat;
- 結(jié)構(gòu)體成員的使用
//用結(jié)構(gòu)體變量引用變量
printf("年齡:%d\n名字:%s\n性別:%c\n",st.age,st.name,st.sex);
//用指針變量的方式引用結(jié)構(gòu)體屬性
struct Student *p = &st;
printf("年齡:%d\n名字:%s\n性別:%c\n",(*(p)).age,(*(p)).name,(*(p)).sex);
#include<stdio.h>
void eat() {
printf("學(xué)生吃飯\n");
}
struct Student {
int age;
char *name;
char sex;
//eatp表示函數(shù)指針的名字
void(*eatp)();
};
int main(void){
//定義一個(gè)結(jié)構(gòu)體變量汉额,并給結(jié)構(gòu)體成員賦值
// struct Student st = {
// 11,"張三",'m'
// };
struct Student st ;
//給結(jié)構(gòu)體內(nèi)的成員賦值
st.age = 12;
st.name = "李四";
st.sex = 'm';
//給結(jié)構(gòu)體指針賦值
st.eatp = eat;
//輸出結(jié)構(gòu)體成員的值
printf("年齡:%d\n名字:%s\n性別:%c\n",st.age,st.name,st.sex);
struct Student *p = &st;
printf("年齡:%d\n名字:%s\n性別:%c\n",(*(p)).age,(*(p)).name,(*(p)).sex);
printf("年齡:%d\n名字:%s\n性別:%c\n",p->age,p->name,p->sex);
//調(diào)用結(jié)構(gòu)體指針。
p->eatp();
return 0;
}
文件處理
- 文件處理流程
- 打開文件
- 文件的處理
- fwrite寫入
- fread讀取
- rewind 將文件游標(biāo)指向文件的開頭
- 關(guān)閉文件
示例代碼
//文件的操作
#include<stdio.h>
int main(void){
//定義一個(gè)文件類型的變量
FILE *file = NULL;
//打開文件
//FILE *fopen(char *filename, char *type);
//a+ 表示 append 追加 +表示如果沒有這個(gè)文件就創(chuàng)建一個(gè)文件
file = fopen("hello.txt","a+");
char *str = "hello world";
//int fwrite(void *ptr, int size, int nitems, FILE *stream);
fwrite(str,1,11,file);
//將文件指針重新指向一個(gè)流的開頭
//int rewind(FILE *stream);
//因?yàn)槲募拇蜷_mode是 a+ 追加的方式躏敢,所以要將處理文件的指針指向一個(gè)流的開頭闷愤。才能讀取到數(shù)據(jù)
rewind(file);
char buf[1024] = {};
//讀出來
// int fread(void *ptr, int size, int nitems, FILE *stream);
fread(buf,1,11*2,file);
printf("讀出來的數(shù)據(jù)是%s\n",buf);
fclose(file);
return 0;
}
/*
"r" = "rt"
打開一個(gè)文本文件,文件必須存在件余,只允許讀
"r+" = "rt+"
打開一個(gè)文本文件讥脐,文件必須存在,允許讀寫
"rb"
打開一個(gè)二進(jìn)制文件啼器,文件必須存在旬渠,只允許讀
“rb+”
打開一個(gè)二進(jìn)制文件,文件必須存在端壳,允許讀寫
"w" = “wt”
新建一個(gè)文本文件告丢,已存在的文件將被刪除,只允許寫
"w+" = "wt+"
新建一個(gè)文本文件损谦,已存在的文件將被刪除岖免,允許讀寫
“wb”
新建一個(gè)二進(jìn)制文件,已存在的文件將被刪除照捡,只允許寫
“wb+”
新建一個(gè)二進(jìn)制文件颅湘,已存在的文件將被刪除,允許讀寫
"a" = "at"
打開或新建一個(gè)文本文件栗精,只允許在文件末尾追寫
"a+" = "at+"
打開或新建一個(gè)文本文件闯参,可以讀瞻鹏,但只允許在文件末尾追寫
“ab”
打開或新建一個(gè)二進(jìn)制文件,只允許在文件末尾追寫
“ab+”
打開或新建一個(gè)二進(jìn)制文件鹿寨,可以讀新博,但只允許在文件末尾追寫
對(duì)于文件使用方式有以下幾點(diǎn)說明:
1) 文件使用方式由r,w,a,t,b,+六個(gè)字符拼成脚草,各字符的含義是:
r(read): 只讀
w(write): 只寫
a(append): 追加
t(text): 文本文件赫悄,可省略不寫
b(binary): 二進(jìn)制文件
*/
C語言的編譯器
編譯命令
編譯 c 代碼,不同的操作系統(tǒng)使用的編譯工具不一樣馏慨,Mac OS 使用的 clang 涩蜘,Linux 使用的是 gcc
gcc/clang -g -o main main.c -I 關(guān)聯(lián)的頭文件位置 -L 關(guān)聯(lián)的庫(kù)位置 -l關(guān)聯(lián)的庫(kù)名字 -l關(guān)聯(lián)的庫(kù)名字2...
編譯過程
.c (預(yù)處理) -> .i 文件(編譯) -> .s(匯編) -> .o(鏈接) -> 可執(zhí)行文件
預(yù)處理
獲取預(yù)處理后的.i文件
- 編寫一個(gè) helloworld.c 文件
- gcc 進(jìn)行編譯
- gcc -o helloworld.i helloworld.c -E 在當(dāng)前目錄下得到一個(gè) hellowold.i 文件
helloworld.i文件
查看這個(gè)文件可以知道,在 .c 預(yù)處理之后的 helloworld.i 文件可以看出熏纯,源碼會(huì)被保留,并且會(huì)將頭文件的內(nèi)容寫到出頭文件的內(nèi)容上面粤策。
- 編譯關(guān)聯(lián)庫(kù)的c文件為add.out
clang -c add.c -o add.out
- 編譯靜態(tài)庫(kù)
將 add.out 編譯為靜態(tài)庫(kù) mylib
libtool -static -o libmylib.a add.out
- 編譯帶有main函數(shù)的main.c文件
clang -o main main.c -I . -L . -lmylib
- 執(zhí)行最終編譯后的文件
./main
源代碼如下
- main.c
#include<stdio.h>
#include "add.h"
int main(void){
int result = add(1,2);
printf("輸出結(jié)果是%d\n",result);
return 0;
}
- add.h
int add(int a,int b);
- add.c
#include<stdio.h>
#include "add.h"
int add(int a,int b){
return a+b;
}
宏定義
就是將 #define 定義的內(nèi)容安裝字符串進(jìn)行替換樟澜。
#include<stdio.h>
//宏定義是沒有c語言語法規(guī)則的,因此可以不需要寫;
#define R 10
#define _main int main(
//傳遞參數(shù)
#define N(n) n*10
//定義一個(gè)加法運(yùn)算的宏
//對(duì)比一個(gè)函數(shù)來說叮盘,這樣定義宏的好處是什么秩贰?
//1.它的參數(shù)的類型不限制
//2.返回值類型不限制
#define ADD(a,b) a+b
_main) {
int a = R;
printf("a = %d\n",a);
int b = N(5);
printf("b = %d\n",b);
int c = ADD(5,6);
printf("c = %d\n",c);
float d = ADD(5,6.3);
printf("d = %f\n",d);
//注意:宏定義只是將字符串進(jìn)行替換操作,不會(huì)考慮一些邏輯運(yùn)算符關(guān)系的柔吼,需要手動(dòng)控制毒费。
//2+3*4+5 = 2+12+5 = 19
int e = ADD(2,3)*ADD(4,5);
int f = (ADD(2,3))*(ADD(4,5));
printf("e = %d\n",e);//19
printf("f = %d\n",f);//45 正確的
return 0;
}
類型定義 typedef
一般會(huì)是用于給自定義類型起別名,例如在jni中愈魏,就大量的使用了typedef來將c的類型起別名為java類型觅玻。
typedef int tni;
int main() {
//在預(yù)處理后的.i文件,這里的tni并不會(huì)像宏定義一樣被替換培漏。
tni a = 10;
printf("a = %d\n",a);
return 0;
}
聯(lián)合體
- 可以將不同類型的變量定義在同一塊內(nèi)存空間中溪厘,并且同一時(shí)間只有一個(gè)變量會(huì)被存儲(chǔ)。
- union 關(guān)鍵字
#include<stdio.h>
//1.定義一個(gè)聯(lián)合體data
union data {
int a;
int b;
char c;
};
int main() {
union data _data;
_data.a = 1;
_data.b = 2;
_data.c = 'c';
printf("a = %d\n",_data.a);
printf("b = %d\n",_data.b);
printf("c = %c\n",_data.c);
//取出地址牌柄,每一個(gè)變量存儲(chǔ)的地址都是一樣的
//%p打印地址
printf("a = %p\n",&_data.a);//0x7fff54c929c8
printf("b = %p\n",&_data.b);//0x7fff54c929c8
printf("c = %p\n",&_data.c);//0x7fff54c929c8
//這里只會(huì)輸出最后一個(gè)賦值的元素 也就是char c = 'c' 會(huì)被存儲(chǔ)畸悬、其他兩個(gè)會(huì)被 c 覆蓋
printf("a = %c\n",*(&_data.a));//99
printf("c = %c\n",_data.c);//99
return 0;
}
鏈表
靜態(tài)鏈表
- 預(yù)先定義好一些元素通過指針的方式連接起來的一條鏈子就是鏈表。
- 先進(jìn)后出
#include<stdio.h>
struct weapon {
int price;
int atk;
struct weapon * next;
};
int main() {
struct weapon a,b,c,*head;
a.price = 100;
a.atk = 101;
b.price = 200;
b.atk = 201;
c.price = 300;
c.atk = 301;
//head指向第一個(gè)元素
head = &a;
a.next = &b;
b.next = &c;
//最后一個(gè)元素的next屬性為空
c.next = NULL;
struct weapon *p;
p = head;
while(p!=NULL){
printf("price = %d,atk = %d\n",p->price,p->atk);
//下面兩種方式都行
//p = (*p).next;
p = p->next;
}
return 0;
}
動(dòng)態(tài)鏈表
動(dòng)態(tài)的往鏈表中添加數(shù)據(jù)
#include<stdio.h>
//mac下<malloc.h>頭文件找不到珊佣,具體原因未查明
#include<mm_malloc.h>
struct weapon {
int price;
int atk;
struct weapon *next;
};
struct weapon * create() {
struct weapon *head;//指向第一個(gè)元素
struct weapon *p1,*p2;//p1指向當(dāng)前輸入的元素蹋宦,p2指向上一個(gè)元素
int n = 0;//表示輸入的元素的次數(shù)
p1 = p2 = (struct weapon*)malloc(sizeof(struct weapon));
//p1->atk = 10;
//p1指向的是一個(gè) weapon 結(jié)構(gòu)體
//我們向往這個(gè)結(jié)構(gòu)體里面的屬性賦值,首先就需要取出結(jié)構(gòu)體的元素的地址: &(p->price)
//下面兩種輸入方式都可以
scanf("%d,%d",&(p1->price),&p1->atk);
head = NULL;
while (p1->price!=0) {
n++;
if(n == 1){
//第一次輸入咒锻,保存到head中
head = p1;
}else{
p2->next = p1;
}
//p2指向上一個(gè)元素(相對(duì)下一次循環(huán)來說的)
p2 = p1;
//給p1重新分配內(nèi)存空間
p1 = (struct weapon*)malloc(sizeof(struct weapon));
scanf("%d,%d",&p1->price,&p1->atk);
}
p2->next = NULL;
return head;
}
int main(){
struct weapon * head = create();
while (head!=NULL) {
printf("price = %d,atk = %d\n",head->price,head->atk);
//下面兩種方式都行
//p = (*p).next;
head = head->next;
}
return 0;
}
工具
我們?cè)谑褂?c 語言
開發(fā)時(shí)冷冗,不可能將所有的 API
都記住,因此我們應(yīng)該常備 API
文檔在身虫碉,在 Mac
平臺(tái)比較好用的應(yīng)該就是 Dash
了贾惦。
Dash
可以下載不同語言的 API
文檔,非常方便,推薦大家使用.