程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C >> C語言入門知識 >> C基礎補習

C基礎補習

編輯:C語言入門知識
1:
gcc 默認編譯生成 a.out----可以自己指定

調試信息:直觀的打印輸出信息
printf("FILE = %s,LINE = %d,func = %s\n",__FILE__,__LINE__,__func__);
__FILE__ :當前文件名 --%s
__LINE__ :當前行號 --%d
__func__ :當前函數 --%s

2:關鍵字:
數據類型關鍵字:(5)
void
char
int
float
double
類型修飾關鍵字: (4)
short
long
signed
unsigned
復雜類型關鍵字: (5)
struct
union
enum
typedef
sizeof
存儲級別關鍵字: (6)
auto
static
register
extern
const
volatile
流程控制關鍵字: (4)
return
continue
break
goto
分支結構關鍵字: (5)
if
else
switch
case
default
循環結構關鍵字: (3)
for
do
while

3:數據類型長度:sizeof() 字節數

基本類型 (32位系統) (64位系統)
char : 1 1
short (int) : 2 2
int : 4 4
long (int) : 4 8
long long (int): 8 8
char * (指針) : 4 8
float : 4 4
double : 8 8

4:數據類型轉換:

double ← float

unsigned long ← long

unsigned ←unsigned short

int ← char , short

5:進制標識:

八進制: 0開頭
二進制: 0b開頭
十六進制: 0x開頭

6:負數在計算機內的存儲格式:

1.先忽略負號直接將其絕對值轉換為二進制
2.再取反二進制

3.最後再加1就是其存儲值

 

int c = -7;//0x000000007(絕對值)---0xfffffff8(取反)---0xfffffff9(加1)

printf("signed c = %#x\n",c);

 
結果是:

signed c = 0xfffffff9

 

7:小數(float double)的存儲方式:

符號位 指數位底數位
float 1 + 8 + 23
double 1 + 11 + 52

8.25
1.整數部分:8---1000
8 / 2 = 4 -- 0 ↑
4 / 2 = 2 -- 0 ↑
2 / 2 = 1 -- 0 ↑
1 / 2 = 0 -- 1 ↑
2.小數部分:0.25 -- 01
0.25 * 2 = 0.5 --0 ↓
0.5 * 2 = 1 --1 ↓
3.結果是:1000.01
4.轉換為符號和指數與底數模式
1.00001 * 2^3 (底數:00001 指數:3 + 127(01111111) = 130 --- 10000010)
符號位1 指數位8位底數位23
5.計算機存儲數據為-- 0 10000010 0000 1000 0000 0000 0000 000

 

-8.25

符號位1 指數位8位底數位23
計算機存儲數據為-- 1 10000010 0000 1000 0000 0000 0000 000
舉例:
-12.5
第一步:1100.1 = 1.1001 * 2^3
第二步:符號位 1,指數: 3 + 127 = 130 (10000010),底數: 1001
第三步:1100 0001 0100 1000 0000 0000 0000 0000
第四步:0xc1 48 00 00
17.625
第一步:10001.101 = 1.0001101 * 2^4
第二步:符號 0 ,指數: 4 + 127 = 131 (10000011) 底數: 0001101
第三步:0100 0001 1000 1101 0000 0000 0000 0000
第四步:0x 41 8D 00 00

8:>> <<
>>(算術右移)
低位拋棄
無符號數:高位全部補0
有符號數:負數(高位全部補1),正數(高位全部補0)
int c = -7;//0x000000007(絕對值)---0xfffffff8(取反)---0xfffffff9(加1)
int d = 1000;//0x0000003e8(絕對值)

printf("signed c = %#x\n",c >> 4);
printf("signed d = %#x\n",d >> 4);

結果是:
signed c = 0xfffffff9
signed d = 0x0000003e

<<(算術左移)
低位全部補0

9:需要注意的運算符號:

表達式1 && 表達式2 (若1為假將不執行2)
表達式1 || 表達式2 (若1為真將不執行2)


異或
a^a=0;
a^0=a;


三目運算符:
表達式1 ? 表達式2 : 表達式3--(若1為真將返回2,否則返回3)


逗號:
表達式1,表達式2,,表達式3,......,表達式n (最後的值是表達式n的值)


10:for循環執行步驟:


for(int i=0;i<20;i++){

循環體
}


執行步驟是:
1、i=0 初始化初值;
2、進行條件判斷i是否<20,如果條件為真,則繼續執行;
3、執行循環體的代碼;
4、i++ 變量i自增一次;
5、回到第2步,一直循環,直到第2步條件為假時, 退出循環,結束


11:printf()函數注意點:


printf(""); (運算從右到左,打印輸出從左到右)

比如:
int i = 10;
printf("%d,%d\n",i++,i);

1.其先從右邊計算i為10
2.再計算i++表達式值為10
3.結果輸出為10,10
------------------------
int i = 10;
printf("%d,%d\n",i,i++);

1.其先從右邊計算i++為10,但是i變成了11
2.再計算i已為11

3.結果從左邊開始輸出為11,10

 

12:三大結構:
順序,選擇,循環

switch(表達式){//表達式的結果必須是整型值或者字符型值,否則報錯

case 'a' :
....;
break;
case 1 :
....;
break;
......
default :
....;
}
-----------------
if(){

} else if (){

} else {

}
------------
循環語句:
while(){} //先判斷再執行

do{}while(); //先執行一次再判斷

for(;;){} //執行步驟在 九

continue;(結束本次循環,繼續下次循環)
break;(跳出當前整個循環)
return;(結束當前函數,並且返回一個值)


13:變量的存儲周期與作用域:

普通局部變量:存儲周期(模塊內,模塊執行完成就銷毀),作用域(模塊內)
普通全局變量:存儲周期(整個程序期間),作用域(所有模塊)

靜態變量(static修飾):不同模塊定義的將會被存儲在不同區域,也就是代表不同變量
局部:存儲周期(整個程序期間),作用域(模塊內)
全局:存儲周期(整個程序期間),作用域(所有模塊)


局部變量 會 局部屏蔽 同名 的 全局變量

比如:


int a = 1;//全局變量

int main(){

printf("%d\n",a);

int a = 10;//屬於main{}內的局部變量,它將在main中屏蔽前面定義的int a=1

printf("%d\n",a);

{//這大括弧不加的話,系統報錯,認為有兩個cc同時存在
int a = 100; //這個a就是{}局部變量,只在這個{}之間使用
printf("%d\n",a);
}
a++;
printf("%d\n",a);

return 0;
}
結果輸出是:
1
10
100
11


14:指針:


int a = 10;
(int *)p = &a; (int *) 為指針符號

b = *p; (此時*p為 a的值,表示將a賦給b)
*p = 100; (此時*p指向a,代表a的存儲空間,以後a就為100)


指針的類型: int *
指針指向的類型: int

野指針:
指針定義後若沒有指向變量地址,
此時它就只是一個單一變量,
其值隨機,
訪問它非常危險!
int *p = NULL;(避免危險,空指針不能訪問)


char *p, t; (表示p為指針變量,t表示char 變量)
char *p, *t;(表示p與q都是指針變量)

15:指針的運算:


int a[10];//數組在內存中存儲是連續的

int *p = &a[0];
int *q = &a[8];

p + 1;
p + 1 - 1;

指針 加減(地址偏移) (偏移量:n * sizeof(*p)---與指針指向地址存儲數據類型有關)
p++,++p (*p++,*++p)
p--,--p (*p--,*--p)
p - q, q - p (個數(矢量-有正負))

(*p)++;
表示將p指向地址空間的內容加 1,
以後p指向的地址空間的內容在隨後使用中都被改變了的,
指針沒有發生偏移
*p++; //*(p++)
先取出當前指針指向內存地址的數值,
再將p指向的地址偏移一個存儲單位
*++p; //*(++p)
先將p指向的地址偏移一個存儲單位
再取出偏移後指針地址的數值,

注意:運算過程中 p 與 *p 的區別運算符的優先級等

16:內存開辟函數:

(void *) 解引用之前必須先類型強制轉換

比如:
int main(){

int a = 10;
void *p = NULL;

p = &a;//任何指針都可以賦給void *指針

printf("%d\n",*p);//該語句錯誤,*p 在這裡直接解引用了一個void *指針
printf("%d\n",*(int *)p);//這裡是先將void *指針p強制轉換為int *指針,再對其解引用,無誤

return 0;
}

下面的這些函數返回值都是定義的void * ,所以在解引用時要先強制轉換

p = malloc(a);
分配a個字節空間
返回這個空間的地址指針 p
這個空間沒有初始化,其值為隨機數,常使用memset(p,20,'0')來初始化
若a = 0 則返回 NULL 或者一個確定的唯一指針,讓free釋放

free(p);
釋放函數創建的指針p指向的空間

p = calloc(a,b);
分配a個b大小的存儲空間
返回這個空間的地址指針p
初始化為0
若a = 0 則返回 NULL 或者一個確定的唯一指針,讓free釋放

p = realloc(q,a);
重新分配大小
返回原有地址指針

比如:
int main{

int *p = NULL;

p = (int *)malloc(100);//因為malloc返回的是void * 類型,這裡就將它強制轉換為int * 了,後面可以直接使用

*p = 100;

printf("%d,%p\n",*p, p);

return 0;
}

17:指針指向 字符串常量 與 字符數組 的區別:


char *s = "12345";
s就是指向一個字符常量"12345",
常量是不能夠修改的,所有s[2]='e';或者*(s+1) = 'e';的操作都是錯誤的

char s[10] = "12345";
char *p = s;
s 是一個字符數組,可以修改
此時指針p可以去修改了,*(p+1)='e'; 相當於s[1]='e';


18:二級指針:


必須存儲一個地址的地址(也就是一級指針的地址)
比如:
int a =10;
int *p = &a; //p存儲的是a的地址,指向a
int **q = &p; //q存儲的是p的地址,指向p

*q 表示解引用,取p存儲的值,也就是a的地址
*(*q) 同理

19:指針數組:

定義的數組元素為指針

(char *)b[10];
數組b的類型為指針類型,
也就是說數組b中定義的10個元素都是指針類型

char *b[10] = {"abcd","hello","efgh"}; 特別注意點: 其中元素都是字符串常量,只能根據地址讀取,不能修改其值
上面語句相當於:
char *b[10] = {NULL};

b[0] = "abcd"; //b[0]指向字符串首地址
b[1] = "hello"; //b[1]指向字符串首地址
b[2] = "efgh"; //b[2]指向字符串首地址

也就是將指針指向一個個字符串常量,常量是不能修改的,只能讀取
*(b[2]) = 'q';這樣的賦值語句就會出現段錯誤
----------------------
可以修改為如下:
char *b[10] = {NULL};

char s1[4] = "abcd";
char s2[5] = "hello";
char s3[4] = "efgh";

b[0] = s1;
b[1] = s2;
b[2] = s3;

此時是將指針指向字符數組,就可以通過指針修改數組!
*(b[0] + 1) = 'd';就s2[1]修改了
*(*(b + 2) + 2) = 'a';就將s3[2]修改了
----------------------------------

b[0],b[1],b[2]....都是代表對應的首地址
比如:
b[0]--(&a), b[1]--(&h)
b[0]+1,b[0]+2,b[1]+1,b[1]+2...也是代表地址(地址偏移)
比如:
b[0]+1--(&b), b[0]+2--(&c), b[2]+1--(&f)

b,b+1,b+2,b+3...都是代表 地址的地址
比如:
b--(&b[0]), b+1--(&(b[1])), b+2--(&(b[2]))


所以有:
*(b+3) :b+3 代表b[3]的地址(&b[3]),對其解引用 b[3](還是地址) 則 *(*(b+3)) 為 *b[3] (NULL (自動補0的原因))



20:數組指針:


指向一維數組的指針 int (*p)[];

int a[3][4];
int (*p)[4];

將二維數組a[3][4]看做
a[0]:(a[0][1],a[0][1],a[0][2],a[0][3])
a[1]:(a[1][1],a[1][1],a[1][2],a[1][3])
a[2]:(a[2][1],a[2][1],a[2][2],a[2][3])

其中a[0],a[1],a[2]都是表示其對應的一維數組的首地址
a可以看做 數組a[0],a[1],a[2]的首地址,a指向a[0]


a[0],a[1],a[2]也都表示首地址

p代表a[0]地址,p+1代表a[1]地址,p+2代表a[2]地址
a[0]+1代表a[0][1]地址 ,a[2]+2代表a[2][3]地址
*(a[0]+1)表示a[0][1]
*(*(p+1)+1) 表示a[1][1]

舉例
int main(){

int a[3][4] = {{1,2,3,4},{5,6,7,8},{9,0,10,11}};
int (*p)[4] = a;

printf("%d,%d,%d\n",**p, *(*(p + 1) +1), *(*p + 1));

//*p指向a的首地址,**p就是a[0]數值
//p + 1 指向a[1]的首地址,*(p + 1)就是a[1]的首地址,再對其加1,地址偏移到a[1][1],解引用為a[1][1]
//*p指向a[0]的首地址,*p + 1指向a[0][1],*(*p + 1))就是a[0][1]
return 0;
}

21:絕對地址寫數據:


向絕對地址 0x12345678 寫入字符 'a'
char *p = (char *)0x12345678;//強制轉換
*p = 'a';

向絕對地址 0x12345678 寫入int型 1000
char *p = (int *)0x12345678;
*p = 1000;

22:


extern 聲明外部變量

static :
修飾全局變量:
只能在該模塊被使用 (普通全局變量在整個程序中能被任意模塊使用)
修飾局部變量:
生命周期變為靜態 (普通局部變量的生命周期為定義到該模塊結束)
修飾函數:
該函數只能在該模塊被調用


const :
修飾變量:
常變量,該變量只能在定義的時候初始化,其他任何時候都不能直接去修改它的值
可以通過指針去間接修改
比如:
int main(){

int const a = 10;
int *p = &a;

a = 100;//該語句會提示錯誤,因為a是常變量
*p = 100;//指針可以修改,但是會提示警告

printf("%d\n",a);

return 0;
}

const全局變量存儲在全局存儲空間,其值只有可讀屬性,不能修改;

比如:
int const a = 10;
int *p = &a;

int main(){

a = 100;//該語句會提示錯誤,因為a是常變量
*p = 100;//該語句也會錯誤

printf("%d\n",a);

return 0;
}

const局部變量存儲在堆棧中,可通過指針修改其值;

const變量在預處理是處理,編譯器只對其值讀取一次。

修飾函數:其他模塊不能使用該函數

const int *p;
const 修飾的是 指針*p 代表地址存儲的值,也就是說指針p指向的地址內的值不能通過 指針p 去修改
但是 p 可以重新指向其它地址

int *const p;
const 修飾的是 變量p 代表地址,也就是說 p指向的地址不能改變,也就是說不能將這個指針變量指向其它地址
但是可以通過 *p 來修改其地址內的值

volatile :

23:函數傳參:

單向的值傳遞--傳入的值在函數中改變後,在退出函數後,改變的值不會返回
int main(){

int a =10;
int b = 100;

printf("%d,%d\n",a,b);//10,100

fun(a,b);//11,101--函數裡面對a,b進行處理,但是離開這個函數就沒有影響a,b的值

printf("%d,%d\n",a,b);//10,100

return 0;
}
int fun(a,b){

++a;
++b;
printf("%d,%d\n",a,b);

return 0;
}

傳地址:傳首地址與數據個數--改變了傳入的值

int main(){

int a = 10;
int b = 100;

printf("%d,%d\n",a,b);//10,100
fun(&a, &b); //11,101 --通過地址改變值
printf("%d,%d\n",a,b); //11,101 --結果都改變了,雙向傳輸

return 0;
}
int fun(int *p, int *q){

*p = 11;
*q = 101;
printf("%d,%d\n",*p, *q);

return 0;
}


24:sizeof() 字節數 注意點:

int a = 10;
int b[10] = {1,2,3,4};
char *c = "12345";

printf("%d,%d,%d\n",sizeof(a), sizeof(b), siezof(c));
//sizeof(a) :a是int 類型 4字節
//sizeof(b) :b數組10個int類型的元素 4 * 10= 40 字節
//sizeof(c) :c代表字符串首地址,是一個地址,32位系統地址 4字節

特別的:
int fun(int a[100]){//這裡的int a[100] 等價於 int *a

printf("%d\n",sizeof(a)); //所以也是一個地址 4字節

return 0;
}

25:

printf(); 輸出到終端
i,d :十進制整數
x,X :十六進制無符號整數
o :八進制無符號整數
u :無符號十進制整數
c :單一字符
s :字符串
e,E :指數形式浮點小數
f :小數形式浮點小數
g :e和f中較短一種
%% :% 本身
------------
m :數據寬度,m小於實際長度將按照實際長度輸出
.n :
對實數:指定小數位
對字符串:指定實際輸出位
- :左對齊
+ :有符號數正數顯示+號
0 :左邊補0
# :加0,0x
l :指定輸出精度
-------------
int a = 100;

printf("%#5.3lx\n",a);


fprintf();輸出到文件
sprintf(); 指定格式轉換到指定字符串
snprintf();


atoi(); 指定字符串轉換為int
int main(){

char *s = "1357";//若char *s = "13a7";會自動切斷a和其後面數據,輸出13
int ss = 0;

ss = atoi(s);
printf("%d\n",ss);//輸出 1357

return 0;
}

atol(); 指定字符串轉換為long
atoll(); 指定字符串轉換為long long
atop(); atoll()函數的老式名稱


  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved