C語言概述
知識掃盲
CPU從內容讀取指令,執行相應的操作.
計算機只能識別1和0指令
第一個C語言程式
連線:就是把我們自己寫的.c編譯後的.o檔案和系統函式合併在一起,生成一個可執行檔案
編譯:
分步編譯:
cc -c xx.c
cc xx.o -o name
快速編譯:
cc xxx.c -o name //快速編譯,不會報錯
執行:
./name
檔名:
.c 原始檔
.o 編譯後的檔案 (windows .obj)
.out 可執行檔案 (windows .exe)
基本語法
關鍵字
就是c語言提供的有特殊含義的符號,也叫做保留字
識別符號
就是我們自己定義的一些符號和名稱,用來區分某些東邪,比如函式名,變數名
識別符號命名規則
- 只有26個字母,數字,下劃線組成
- 嚴格區分大小寫
- 不能以數字開頭
- 不可以使用關鍵字
- 有意義
註釋
註釋用來表明一段程式碼有什麼作用,不會被編譯,還可以用來排錯
可以寫任何文字,一般用豆綠色
單行註釋: // 可以巢狀
多行註釋: /* */ 不可以巢狀
資料
靜態資料
永久性的資料,一般存在硬碟
硬碟的訪問速度很慢
動態資料
在程式執行過程中,動態產生的臨時資料,一般儲存在記憶體中.
斷電就會消失.
記憶體訪問非常快
資料型別
- 基本資料型別
- 指標型別
- 構造型別
- 空型別
常量
表示一些固定的資料
整形常量
10 -33
浮點常量
0.33f 0.055
字元常量
'a' 'dd' '+' //字元那個用單引號
字串常量
"fasdfa" "343ed" //字串用雙引號
0.0是小數
變數
一些經常改變的不確定資料,就用變數來表示,比如遊戲積分
定義變數
- 使用之前必須定義
- 變數型別 變數名
- 不同的變數型別佔用不同大小的儲存空間.
- 第一次賦值是初始化
int 4個位元組
char 1個位元組
輸出變數:
printf('%d',name);
佔位符:
%d/$i 整形
%.4f 4為小數
%c 字元
%p 地址
變數在函式中的作用域
在函式裡面:
從開始定義的那個地方開始,一直到函式結束.
函式在呼叫的是偶分配記憶體空間,函式結束的時候釋放記憶體空間
變數在程式碼塊中的作用域
程式碼塊:
int main()
{
{
這就是一個程式碼塊,在這裡面定義的變數,只能在這裡面用
}
//但是在這裡定義的變數可以被程式碼塊中使用
int a = 10;
{
printf('d%',a);
}
}
在程式碼塊中使用變數,優先在自己的快裡找,然後依次往上找
在函式外面定義的是全域性變數,函式內的為區域性變數
程式碼塊用來提高效能的,可以在適當的時候釋放變數,不會一直佔用記憶體
及時回收不再使用的變數
變數的記憶體分析
變數在記憶體中佔據多個位元組:
char 1
int 4
float 4
double 8
記憶體的定址從最大的開始,也就是從底部入棧
變數越先定義,記憶體的地址就越大.
與之對應的變數的地址是變數所佔位元組分配中的最小的一位
可以用&name取得name的地址. 輸出用 &p
只要定義變數,就會分配地址,不管有沒有值
scanf函式
用來接收使用者的輸入,是個阻斷函式,必須完成後,下面的程式才會執行
只接收變數的地址
scanf("%d",&name);
多個引數以分隔符(任意)隔開,在輸入的時候也必須以這個符號隔開
如果以空格隔開,在輸入的時候用tab或者回車也可以
裡面不能寫\n,不然不會停止
運算子
+加法運算 還可以做正號
%去餘 兩邊都是整數 正負數與左邊的值有關
型別轉換
自動轉換
在運算或者賦值時,會自動將大型別轉換為小型別
int a = 19.8; //19
也可能將小型別提升為大型別:
double c = 10.9 +9; //19.9000000
運算結果和參與運算的資料型別有關,兩個整數運算結果必定是整數.
1/3 = 0;
強制轉換
int a = (int)12.3;
符合運算
a += 1+2+3; //等於 a = a+1+2+3;
b = ++a 先自增運算在賦值
b = a++ 先賦值在自增運算
b = (++a) + (a++) //b = 10 + 12
b = (a++) + (++a) //b = 11 + 11
只有變數才允許自增自減
sizeop
獲取一個變數或常量佔據了多少位元組
關係運算子
< <= > >= 的優先順序高於 == !=
邏輯運算子
&& 邏輯與
|| 邏輯或
!邏輯非
三目運算子
int a = 10>5 ? 1:0;
流程控制
條件語句
如果要在if後面定義新的變數,必須用{}括起來
if()
int a = 10; //報錯
if()
printf("%d",a); //對的
在switch語句中,case中也不能定義變數,不然又作用域不明確了,非要定義變數的話,必須用大括號括起來
switch(c){
case '+':
int sum = a + b; //報錯
break;
}
但是可以加個大括號解決
switch(c){
case '+':
{
int sum = a + b; //報錯
break;
}
}
迴圈
white迴圈
continue; 終止本次迴圈,繼續下一次迴圈
break; 結束整個迴圈
如果沒有大括號,就會執行挨著迴圈的那一條語句,注意變數作用域
do while 不管條件怎麼樣,先執行一次,再判斷條件是否成立
注意事項
for(int i=0;i<5;i++){
int i = 10;
printf("%d",i); //會輸出5個 10 ,因為每一次迴圈結束,{}中的i就被釋放了
}
這是對的,因為括號裡的作用域要比{}裡面的大,可以看成for迴圈也是一個區域,而{}是form迴圈區域裡的一起個小塊
函式
格式:
返回值型別 函式名(引數){
函式體
}
形參: 函式括號裡的引數,可有可無
實參: 呼叫函式時傳遞的引數
函式體內不能定義和形參一樣的變數
return 退出函式,返回一個具體的值給函式呼叫者 ,返回的型別必須和定義函式時的型別一樣
也可以沒有返回值,這樣定義函式:
void test(){
return;
} 這個函式也可以用return退出函式;
不寫返回型別,預設返回int型別
就算寫了返回型別,也可以不返回值
注意
函式不能重名
函式不能巢狀定義
函式要定義在main函式前面,因為程式是從上往下執行的
可以這樣解決,把函式放在main的後面,但是要在main的前面寫一個函式宣告:
void test();
int main(){};
voed test(){}
函式可以重複宣告
還可以在呼叫前宣告:
init main(){
void test();
test();
}
編譯的時候只檢查語法
連結的時候會檢查函式存不存在
include
系統自帶的用<>
我們自己寫的用""
多人開發
各自編譯出來各自的之後,然後合併
cc a.o b.o c.o
還可以直接編譯,但是不會提示報錯資訊
cc a.c b.c c.c
使用別人的函式,還在前面寫出宣告,我們可以引入.h檔案
#include "func.h";
一般一個.c檔案都會對應一個.h檔案
main 和 printf
main函式中,return 0,正常退出;return -1,異常退出;
printf也有返回值,是輸出佔用字元的數量
中文佔三個字元
進位制
就是計數的方式
要了解四種進位制: 10 2 8 16
預設就是10進位制
二進位制:
int num = 0b1100; //前面帶個0b就是二進位制
八進位制:
int num = 014; //前面帶個0就是八進位制
十六進位制:
int num = 0xc; //前面帶個0x就是十六進位制
%d十進位制輸出, %o八進位制輸出,%x十六進位制輸出,%p輸出地址,$f輸出小數
變數在記憶體中的儲存細節
任何數字在記憶體中存的都是二進位制
一個int型別佔四個位元組,32bit
0000 0000 0000 0000 0000 0000 0000 0000 (一個位元組八位,32位存4個位元組)
進位制轉換
二進位制轉十進位制
0000 0000 0000 0000 0000 0000 0000 1100 = 02的0次方 + 0的2的1次方 + 12的2次方 + 12的3次方
十進位制轉二進位制
67 = 64 + 2 + 1
= 2的6次方 + 2的一次方 + 2的0次方
= 100011
二進位制的取值範圍
0-2的n次方 -1
int 2的31次方-1 因為第一位是符號位
最高位是0是正數
最高位是1是負數
型別說明符
int 4位元組 %d (32位處理器2位元組)
long 8位元組 %ld (32位處理器4位元組) 4354353425423523453425L
long long 8位元組 %lld (32位處理器8位元組) 34524352345243523452LL
short 2位元組 %sd
sigend 有符號,正數,0,負數
unsigend 無符號 (Int會取值到32位)
同一個型別的修飾符不能同時使用,不同型別的可以同時使用
位運算
按位與: 相同為1 9&5
10101
01011
-----
00001
可以按位與一個1,返回最後一位的值,用來判斷奇偶性,1為奇數,0為偶數
按位或: 任何一個為1就為1 9|8
101010
100101
------
101111
按位異或: 不同為1 9^8
101010
100101
------
001111
按位取反: 0變1,1變0 ~9
左移: 把整數的各二進位制全部左移N位,高位丟棄,低位補0, 左移N位其實就是乘以2的N次方
有時候會把負數搞成正數
右移: 往右移動N位,低位丟失,高位補符號位,右移就是除以2的N次方
Mac高位補符號位,其他看編譯器
char型別
字元在記憶體用是以ASCII碼存的,佔一個位元組
A 65 記憶體中 0100 0001
B 66 記憶體中 0100 0010
列子:
char a = 'A' + 32; // 結果是a
先進行運算 65 + 32 = 97 //就是a了
單引號只能用於一個單位元組的字元
陣列
構造型別: 由無數個基本型別組成的型別,構造型別
定義:
型別 名字[元素個數]
int arrs[5];
arrs[1] = 3;
arrs[2] = 4;
int arrs[5] = {1,2,3,4,5};
int arrs[5] = {[4]=9,[5]=9};
int arrs[] = {3,4,5};
int ages[bian]; //對
int ages[bian] = {3,3,4};//錯 如果在定義的時候初始化,必須寫常量
長度:
int count = sizeof(arrs)/sizeof(int);
陣列放到函式裡,可以省略引數:
void change(int array[]){}
如果在函式修改了陣列元素,原陣列也會改變,因為傳進來的就是一個地址.也就是一個指標,指標永遠是八個長度
所以無法獲取到陣列長度,在傳遞引數的時候,要把長度也傳過去
基本型別是值傳遞
二維陣列:
int arr[2][4] = {
{},
{},
...
}
字串
定義:
char name[] = "adsfdsa"; //預設最後會有一個\0
char name[7] = "adfads";
0的作用:
如果沒有\0,字串就不知道在哪結束,就會把其他的變數也都輸出了,在記憶體中一直往下找,知道找到\0停止
長度
#include <string.h>
strlen("aaa"); //原理就是去說字串的位元組數,也就是指標地址,不包括\0
字串陣列:
name[3][10] = {"fdfd","dfa","adaa"}
指標變數
定義:
變數型別 *變數名
int *p = a; //把a的地址存到了p裡面
指標變數只能儲存地址
指標能根據一個地址值,訪問對應的儲存空間,並進行值的修改
int表示P只能指向int型別的儲存空間
賦值 *p = 10;
取值 *p;
指標使用注意
- 指標變數只能存地址
- 指標變數不能為空,必須要有確定指向的值,指標變數未經過初始化,不要拿來訪問其他儲存空間
- 定義指標時候的*沒有任何意義,僅僅是一個象徵,和後面的*不一樣
- int *p; 代表 (int *)p,僅僅說明p是一個指標型別變數
- p = &a; 把某一個變數的地址給p , 後面再使用*p就是代表a的空間
- *p = 90; 代表的是給a的空間賦值, 而 *p是直接取出a空間的值
- char *cp; 字元指標
指向指標的指標
int a = 10;
int *p = &a; //等於 int *p; p = &a;
int **pp = &p; //等於 int **pp; pp = &p;
此時,
*p
**p
本質上指向的都是變數a的儲存空間,列印他們的地址,都是一樣的
而每個指標變數,還有自己的地址,都是不一樣的
指標型別
%zd, sizeof(*cp);
任何指標都佔用8個位元組的儲存空間
指標的的型別,決定了指標從地址開始取多少個位元組
指標與陣列
int ages[5] = {34,56,65,67,34};
int *p;
p = &ages[0]; //或者 p = &ages;
p++; // 或者p+1;
printf("%d\n",*p);
把陣列的一個元素的指標給p,
p儲存的是指標型別,是一個地址,那麼p+1,就是一個地址加上整形1,由於整形1佔4個位元組,那麼p所指向的
地址也加四個位元組,又因為整形佔四個位元組,所以p+1相當於指標p指向陣列的元素地址向下移動了一個位置,
也就變成下一個元素了
指標變數的+1究竟加多少,取決與指標的型別:
int * 4
char * 1
double * 8
把陣列給一個指標變數,就是把陣列的第一個元素的地址給了指標變數
指標與字串
char name[] = "it";
char *name2 = "it"; //把一個字串給一個指標變數,參照陣列,其實就是把字串的第一個字元的地址給了指標變數.
printf("%c\n",*name2); //i
printf("%s\n",name2); //it
在OC中,用的比較多的就是指標操作字串
陣列定義的字串是在棧中,它的值是隨便可以更改的 (字串變數)//使用場合,字串的內容需要經常修改
而指標指向的字串,是放在常量區,值不可以更改 (字串常量) //經常用到的不需要修改的字串
*name2 = "it";
*name3 = "it";
他們的地址是一樣的
也就是說,放在常量區的會有一個快取,我們下次再用的時候就不會再開闢一個儲存空間了
指標陣列: char *name[5] = {"aaaa","baaa","caaa","daaa","eaa"}
返回指標的函式
char *test()
{
return "json";
}
指向函式的指標
//void代表函式的返回值型別
//固定寫法 (*p)代表 這是一個函式指標
//() 指標指向的函式有沒有形參
void (*p)();
p = test; //把test函式的指標給P
(*p)(); //這樣就可以直接代用函式 (*p)取出函式 ()呼叫函式
p(); //這樣也可以呼叫函式 p已經代表函式的指標了
test(); //這樣原始呼叫
其他資料型別
變數型別
區域性變數:
定義在函式或者程式碼塊中的變數,函式和程式碼塊一執行完就銷燬
全域性變數:
定義在檔案中,程式執行完銷燬
結構體
構造型別:
陣列 只能有一種資料型別
結構體:
可以有混合型別
結構體型別是不存在的,要我們自己定義:
struct Person //定義型別
{
int age;
double height;
char *name;
}
struct Person p = {20,1.55,"jack"}; //定義變數
p.age = 30; //還可以這樣定義值
struct Person p = {.age = 30,.height = 1.44,.name = "rose"}; //還可以這樣定義
記憶體分析:
定義結構體並不會分配儲存空間
一個結構體佔的空間,是所有成員的和
對其演算法,結構體所佔的儲存空間,一定是最大成員所佔位元組數的倍數
定義結構體的多種方式
上面說了一種,還有一種
struct Person //定義型別
{
int age;
double height;
char *name;
} p; //定義變數;
不可重用的方式:
struct //定義型別
{
int age;
double height;
char *name;
} p; //定義變數;
型別的作用域
和變數的作用域差不多
結構體陣列
struct Person pes[3] = {
{},
{},
{}
};
指向結構體的指標
struct Person *p;
p = &Person; //指標變數想儲存誰,就把誰的地址拿過來存起來
第一種方式:
Person.age;
第二種方式:
(*p).age;
第三種方式:
p->age;
巢狀定義:
struct Person //定義型別
{
int age;
double height;
char *name;
struct Date a;
struct Date b;
};
列舉型別
定義列舉型別
enum Season
{
spring, //0
summer, //1
autumn, //2
winter //3
}
定義列舉變數
enum Season s = spring;
總結
基本資料型別
int
long int, long: 8位元組 %ld
short int, short: 2 %d %i
unsigned int, unsigned: 4 %zd
signed int, signed , int:4 %d %i
float\double
float: 4 %f
double: 8 %f
char
1位元組 %c %d
char型別儲存在記憶體中的是它的ASCII值
'A' => 65
構造型別
陣列
只能由同一種型別的資料組成
定義: 資料型別 陣列名[元素個數]
結構體
可以由不同型別的資料組成
先定義結構, 再利用型別定義變數
指標型別
變數的定義
int *p
指標賦值
p = &age;
取值
*p;
修改值
*p = 22;
列舉型別
當一個變數只允許有幾個固定取值時,可以使用列舉
其他
預處理指令
把程式碼翻譯成0和1之前執行的指令(編譯)
所有的預處理指令都是以#開頭
位置隨便寫(也是有作用域的,從編寫指令的那個位置開始,一直到檔案結束)
巨集定義
全域性定義
#define COUNT 6 //也就是常量了,不過在編譯之前就會執行,這樣陣列使用不會報錯了
int age[COUNT] = {1,2,3,4,5,6};
失效
#undef COUNT;
帶引數的巨集定義
#define sum(v1,v2) (v1+v2) //不加括號在用的時候會亂
條件編譯
按條件去編譯
#if (A == 5)
...
#elif (A == 6)
...
#endif
...
判斷有沒有定義巨集
#if define(MAX) //#ifndef MAX
...code...
#endif
...
檔案包含
<>表示系統自帶的檔案, ""表示自定義的檔案
不允許迴圈包含 A包含B,B包含A
解決多次包含只解析一次:
#ifndef A
#define A 123 //可以不寫值,定義一個空的巨集
int sum () {}
#endif
一般以檔名為巨集名
關鍵字
typedef
int
typedef int MyInt; //給int起個別名
char *
typedef char * String; //給字串指標一個別名
String name = "asdf";
結構體
typedef struct Student Mystu; //給結構體定義一個別名
Mystu stu1,stu2;
還可以
typedef struct Student
{
int age;
} Mystu;
還可以
typedef struct //只能用Mystu訪問了, 上面那種方法還可以使用原本的方式訪問
{
int age;
} Mystu;
定義列舉型別,也可以參照結構體
指向函式的指標
typedef int (*MyPoint)(int, int);
Mypoint p = sum;
Mypoint p2 = minus;
原本是
int (*p)(int ,int) = sum;
指向結構體的指標
typedef struct Per * PersonPoint;
PersonPoint p = &p;
還可以用巨集定義來代替
#define String char *; //但是隻能單個使用,不能 String s1,s2;
static 和 extern
對函式和變數有效
對函式
外部函式:本文字和其他文字都可以訪問,預設 extern省略
內部函式:只能被本文字訪問,其他檔案不能訪問 static
兩個檔案都有同樣名字的內部函式,是不衝突的
對變數
全域性變數分兩種:
外部變數:本檔案和其他檔案都可以訪問 預設 extern ()
不同檔案中的同名變數,都代表同一個變數
內部變數:只有本檔案可以訪問 static
static 定義了一個內部變數
extern 宣告瞭一個外部變數
static 對區域性變數的作用
static修飾的區域性變數(函式內) 函式結束變數不釋放,一直到程式結束
並沒有改變區域性變數的作用域
多次執行函式,變數的值一直在變,所有的函式共享這個變數
遞迴
其他補充
指標的實質就是儲存了一個地址
指標在64位系統中佔8個位元組
因為64的系統,記憶體地址有2的64次方,所以要8個位元組去儲存
在分配記憶體的時候,gcc編輯器會做一個優化,優先分配同一型別的變數,
而不是按照程式碼的編寫順序進行分配
記憶體結構,從上到下:
系統記憶體 系統使用
棧記憶體 儲存的函式執行的狀態,區域性變數值
可分配記憶體 由應用程式自由調配
堆記憶體 程式設計師自己分配
資料段 常量,全域性變數,和靜態變數
程式碼段 程式碼 a + b
array本質上是一個指標常量,地址固定
P指標是一個指標變數,位置可以隨便移動 也可以用p[i]取值
相關文章
- 第二章 C語言概述C語言
- C_Primer第2章 C語言概述C語言
- Java語言概述Java
- JSP 表示式語言概述JS
- 自然語言處理(NLP)概述自然語言處理
- Flutter系列之Dart語言概述FlutterDart
- C語言C語言
- 聊聊C語言/C++—程式和程式語言C語言C++
- Java開發之路—java語言概述Java
- go語言編譯過程概述Go編譯
- C語言字串C語言字串
- C語言(一)C語言
- C語言: returnC語言
- C語言 typedefC語言
- C語言與嵌入式C語言的區別C語言
- C語言學習方法,怎麼學習C語言?C語言
- go語言與c語言的相互呼叫GoC語言
- 1901:The C programming language !(C語言)C語言
- C語言教程——03 C語言結構C語言
- C語言:extern用法C語言
- C語言版本迭代C語言
- C語言 截圖C語言
- C語言 - 字串拼接C語言字串
- C語言加強C語言
- c語言複習C語言
- c語言入門C語言
- C語言位操作C語言
- 初識C語言C語言
- c語言筆記C語言筆記
- C語言基礎C語言
- c語言作業C語言
- C語言 共用體C語言
- C語言 備份C語言
- C語言指標C語言指標
- Linux-C語言LinuxC語言
- c語言_遞迴C語言遞迴
- C語言陣列C語言陣列
- 安全是一門語言的藝術|威脅調查分析語言概述
- Java 語言概述與開發環境(1)Java開發環境