經驗:窗體與窗體間,窗體與服務間傳遞訊息的方法
初識C語言
大家好,我是新來的菜鳥,我來開始給大家講解自己理解的C語言。講的不好還望不要噴的太厲害了(手動滑稽)。謝謝大家的觀看!!!
什麼是C語言?
首先我們講一下啥是語言,人和人不就是用語言交流嘛,那語言種類有很多種比如說漢語呀、英語(想必大家多多少少都學過一點吧,我這種菜鳥就是英語沒學(手動滑稽))、德語或者日語等等其他語言。那我們知道了語言就是用來進行交流的嘛。人和人可以用上面所述的語言交流,但是人和機器呢?那不就是機器語言咯(明知故問)。是的,機器語言,說到機器語言這就得提起我所熟悉的那幾個,組合語言(好像現在很少用了吧?)、C語言、C++、C#(有木有跟我剛見到這個語言一樣的念成c井,其實它叫C sharp)、Java、Python呀,其他的我就好像聽得比較少了吧,畢竟新人嘛,嘿嘿,都體諒體諒一下哈,多謝!迴歸正題什麼是C語言,書上是這樣說的:
C語言是一門通用計算機程式語言,廣泛應用於底層開發。C語言的設計目標是提供一種能以簡易的方式編譯、處理低階儲存器、產生少量的機器碼以及不需要任何執行環境支援便能執行的程式語言。
儘管C語言提供了許多低階處理的功能,但仍然保持著良好跨平臺的特性,以一個標準規格寫出的C語言程式可在許多電腦平臺上進行編譯,甚至包含一些嵌入式處理器(微控制器或稱MCU)以及超級電腦等作業平臺。
二十世紀八十年代,為了避免各開發廠商用的C語言語法產生差異,由美國國家標準局為C語言制定了一套完整的美國國家標準語法,稱為ANSI C,作為C語言最初的標準。 [1] 目前2011年12月8日,國際標準化組織(ISO)和國際電工委員會(IEC)釋出的C11標準
是C語言的第三個官方標準,也是C語言的最新標準,該標準更好的支援了漢字函式名和漢字識別符號,一定程度上實現了漢字程式設計。
C語言是一門面向過程的計算機程式語言,與C++,Java等面向物件的程式語言有所不同。
其編譯器主要有Clang、GCC、WIN-TC、SUBLIME、**MSVC、Turbo C等。
看了這麼多籠統的解釋還是有繁瑣的,所以就這樣認為吧,C語言就是一門計算機程式語言,是人和計算機交流的語言。個人理解有錯誤的話希望大家指點指點,感謝!
第一個C語言程式
說了這麼多概念,還是來點實際的吧,大家可以去網上搜索C語言程式設計軟體,各種各樣的都有,這裡分享一下我使用的軟體,我使用的是Vs2022,下面我給大家演示一下第一個C語言程式:
#include <stdio.h> //標頭檔案的包含 //-->註釋符號
int main()
{
printf("Hello World!\n");
return 0;
}
上面是程式碼塊,我們可以看到這個黑色的框框裡面出現了HelloWorld這幾個字元。
C語言規定每個程式都要主函式(main),C語言程式碼都是從主函式開始執行的。而且主函式有且只有一個哦。stdio.h 是一個頭檔案 (標準輸入輸出標頭檔案) , #include 是一個預處理命令,用來引入標頭檔案。 當編譯器遇到 printf() 函式時,如果沒有找到 stdio.h 標頭檔案,會發生編譯錯誤。return 0; 語句用於表示退出程式。printf()函式是列印函式,就是用來在螢幕上列印資訊的。
資料型別
資料型別什麼是資料型別呢?在生活中有許多不一樣的資料,比如說我有一個朋友他年齡20歲,身高190cm,名字叫“小顧”,或者比如說買個瓶水2元錢,或者是買菜16.9元錢.....等等。我們需要記錄這些不同型別的數字,在計算機中就需要不同的資料型別來表示。而C語言中基本資料型別有這幾種:
char //字元資料型別
short //短整型
int //整形
long //長整型
long long //更長的整形
float //單精度浮點數
double //雙精度浮點數
解釋一下這幾個不同型別吧,char
型別是用來表示一個字元型別的,就比如說字元a。而short int long long long
這幾個都是用來表示整數的,比如說20、100、50000等等。float double
這兩種呢就是用來表示小數的,比如說3.1415....後面列舉不出來了忘了這個圓周率了,只是說它們的精度不同也就是小數點後面的個數不同。那C語言沒有字串型別嗎?也就是像這樣的“i like China”。好像確實沒有明確哪種型別是字串型別,但是有個是用char [12]="i like China"
表示字串型別的。
那既然有這麼多型別,那它們的大小是多少呢?C語言中規定求型別大小是用sizeof關鍵字,大小是位元組
#include <stdio.h>
int main()
{
printf("%d\n", sizeof(char));
printf("%d\n", sizeof(short));
printf("%d\n", sizeof(int));
printf("%d\n", sizeof(long));
printf("%d\n", sizeof(long long));
printf("%d\n", sizeof(float));
printf("%d\n", sizeof(double));
printf("%d\n", sizeof(long double));
return 0;
}
通過觀察我們可以知道
資料型別 | 型別大小 |
---|---|
char |
1 |
short |
2 |
int |
4 |
long |
4 |
long long |
8 |
float |
4 |
double |
8 |
大家第一次看到這個位元組可能有點疑問,其實換個符號大家可能就認識了,byte,大家看見有木有點熟悉,不熟悉的話再換一個KB或者MB、GB這樣大家應該會熟悉了。其實在計算機當中,單位有位元位,位元組這一說,計算機中最小的單位是位元組,而一個位元組是8個位元位。一個位元位很簡單,就是0或者1。
說明一下:為什麼會出現這麼多不同型別呢?
這是因為每個型別所佔的記憶體空間大小不同,所以我們合理使用不同的型別,可以節省記憶體空間,避免浪費嘛,你說是吧(手動滑稽)。
變數和常量
看見這兩個詞會怎麼理解?我用最通俗易懂的話來講:變數就是那些可以被改變的值。常量就是不能被改變的值。
舉個例子:
變數:年齡、體重、薪資、存款等.....
常量:圓周率、身份證號碼、血型等.....
變數
定義變數
在C語言中定義變數的方法是怎樣的呢?
之前瞭解了資料型別是什麼,定義變數那就是想要什麼型別的變數就定義什麼型別的變數。
C語言定義變數的方式是:型別 變數名 = 初始化值;
int age = 20;
float weight = 45.5f;
char ch = 'w';
變數的分類
- 區域性變數 定義在函式體內或者說 在
{}
內部 - 全域性變數 定義在函式體外
#include <stdio.h>
int a = 10;//全域性變數
int main()
{
int a = 100;//區域性變數
return 0;
}
通過上面程式碼大家應該知道了什麼是全域性變數和區域性變量了吧。
看為什麼出錯了呢?雖然a變數是在函式體中,也是區域性變數,但是為什麼就錯了呢?那是因為{}
限制它的作用範圍。再看
這樣又沒有問題了,所以說在函式內部的雖然是區域性變數但是它的作用範圍會被限制的,作用範圍下面會說到的,各位客官請勿著急。
接下來我們看個例子
#include <stdio.h>
int b = 2019;//全域性變數
int main()
{
int a = 2020;//區域性變數
//這裡區域性變數與全域性變數同名了 會發生什麼呢?
int b = 2021;//區域性變數
printf("b = %d\n",b);
return 0;
}
可以看到,當全域性變數與區域性變數同名時候,優先使用的是區域性變數。
變數的使用
我們瞭解了怎麼定義變數,現在我們瞭解怎麼去使用它。
計算兩個數的和,num1,num2,從鍵盤輸入數值,求和然後列印到螢幕上程式碼如下:
#include <stdio.h>
int main()
{
int num1 = 0;
int num2 = 0;
int sum = 0;
printf("輸入兩個運算元:>");
scanf("%d %d", &num1, &num2);
sum = num1 + num2;
printf("sum = %d\n", sum);
//這裡介紹一下輸入,輸出語句
//scanf 輸入語句 顧名思義是向計算機輸入值
//printf 輸出語句 上面有使用過,就是在螢幕上列印一段資訊
return 0;
}
大家如果也是用的是VS2022或者VS2019,在這個程式碼執行時會出現一個錯誤,那就是scanf函式unsafe,意思就是這個函式不安全,這個時候我們可以在後面加上 _s
就變成了scanf_s
,或者大家不想這麼麻煩的話就在原始檔最上面加上define _CRT_SECURE_NO_WARNINGS 1
即可。
上述圖片只是展示了變數的一種用法,後續還會有其他用法,之後用到再說。現在只是初步認識一下C語言。
變數的作用域和生命週期
之前有提到過作用範圍。而接來下要講的作用域就跟作用範圍有關。
作用域
作用域(scope),程式設計概念,通常來說,一段程式程式碼中所用到的名字並不總是有效/可用的
而限定這個名字的可用性的程式碼範圍就是這個名字的作用域 。
可以這樣理解作用域就是作用範圍,個人的理解哈。我們用程式碼形式來觀察
可以看見,程式可以正常執行,那我們看下面:
這下報錯了,為啥呢?這就是作用域的問題了。看完區域性變數,我們來看全域性變數:
可以看見全域性變數作用域是整個工程。得出結論
- 區域性變數的作用域是變數所在的區域性範圍。
- 全域性變數的作用域是整個工程。
生命週期
看見生命週期我們會想到什麼?人的一生?從出生到離開這個世界,這也算是是生命週期。
官方的解釋是:
變數的生命週期指的是變數的建立到變數的銷燬之間的一個時間段。
那變數的宣告週期呢?變數什麼時候建立、什麼時候銷燬,這之間的時間段。
用事實來說話,眼見為實,咱們看程式碼效果圖:
我們之前講作用域時,不輸入下面那條printf()
函式語句時是不會報錯的,但是添加了後為什麼又報錯了呢?這就是這個變數a
的生命週期到了,出了{}
後a變數生命週期到了就自行銷燬了。所以後面的輸出語句執行就會報錯。全域性變數的生命週期應該不用我說了吧(手動滑稽)?跟全域性變數的作用域有關。
小結:
- 區域性變數的生命週期:進入作用域生命週期開始,出作用域宣告週期結束。
- 全域性變數的生命週期:整個程式的生命週期。程式結束了全域性變數生命週期也就到頭了。
常量
常量:不可變的值。C語言中定義常量和定義變數的形式是不同的。
C語言中常量可以分為以下幾種
-
字面常量
-
const
修飾的常變數 -
#define
定義的識別符號常量 -
列舉常量
#include <stdio.h>
#define MAX 1000 //#define的識別符號常量
//列舉常量
enum COLOR
{
RED,
GREEN,
BLUE
}
//RED,GREEN,BLUE皆是列舉常量
int main()
{
//1.字面常量
3.1415926;//浮點型常量
1000;//整型常量
'a';//字元常量
"hello"//字串常量
//2.const修飾常變數
//int num = 20;//變數
const num = 20;
num = 50;//加上const後能否改變?
printf("num = %d\n",num);
return 0;
}
看程式碼作用還是小了點,咱們看點實際點的。
可以看到num
這個變數是可以被改變的,接下來再看:
加上const
就修改不了了,這是怎麼回事?因為被const修飾的變數叫做常變數,具有常屬性,常屬性就是不能被改變的屬性,雖然不能被改變,但是它本質上還是個變數。
我們建立陣列時,[]
內要是個常量,大家可以看到錯誤資訊,這說明n
是個變數。你這不是廢話嘛又沒加上const
修飾。那好我們加上const
修飾一下看看會怎樣?
喏,看見沒,即便加上const
修飾,結果還是一樣的,這證明了什麼,這就證明了const
修飾的變數,本質上還是個變數,只是具備了常屬性,也就是不能被修改的屬性。
再看一個列舉常量,列舉就是一一列出來,像三原色、性別等等可以一一列舉出來的值。它們都是有實際值得。我們也可以打印出來的。
這裡我把#define
定義的識別符號常量也放進去了,定義了列舉常量三原色,並且也一一列舉了,可以看到#define
定義的也是個常量,無法被修改,再看列舉常量它們其實也是有值的,而且還是遞增的值,但能不能改呢?其實不能,但是可以給它賦初值。
可以看到賦初值後,下一個也會跟著上面的初值改動而改動的。
字串+轉義字元+註釋
字串
什麼是字串?在C語言中,字串就是一串字元。怎麼表述的呢?用“”
括起來的一串字元就是字串。
最重要的一點是,字串結束的標誌是一個‘\0’
的轉移字元。因為在計算字串長度的時候‘\0’
是結束標誌,不算做字串的內容,但是用陣列儲存字串的時候,陣列的長度需要將‘\0’
計算上,只是單獨計算字串長度是不計算‘\0’
#include <stdio.h>
int main()
{
//C語言中用字元陣列儲存字串,陣列也就是一組型別相同的元素的集合
char str1[]="hello";
char str2[]={'h','e','l','l','o'};
char str3[]={'h','e','l','l','o','\0'};
printf("%s\n",str1);
printf("%s\n",str2);
printf("%s\n",str3);
return 0;
}
我們來看一下上面程式碼的執行效果:
咦?為什麼第二個跟其他兩個不一樣呀?這就是跟字串結束的標誌有關。我們除錯一下看看這幾個陣列中放的是什麼。
可以看到第一個和第三個陣列放的都是一樣的,而第二個少了個‘\0’
,而且第一個陣列不是5個字元嗎,怎麼多了一個,之前說字串結束的標誌是‘\0’
,因為第一個陣列是用來儲存字串的,所以會預設在結尾加上‘\0’
只是不會顯示出來。但是在計算陣列長度的時候就會把‘\0’
加上去。接著說第二個為啥是這樣的,因為第二個陣列最後一個不是‘\0’
所以會在記憶體中後面去找‘\0’
,找到‘\0’
之前的資料也會被打印出來,所以第二個就是會是這樣列印的。
轉義字元
轉義字元?啥是轉義字元呢?字面意思,轉變意思的字元。
如果我們要在螢幕上列印一個目錄:d:\vs2022\test.c
。我們該如何寫程式碼呢?之前我們用過printf
函式列印資訊。
#include <stdio.h>
int main()
{
printf("d:\vs2022\test.c\n");
return 0;
}
為啥跟我們期待的不一樣呢?我們看程式碼上面有語法高亮,\v \t
跟\n
這個都改變了顏色,說明它們是一樣型別的。之前列印其他資訊的時候會看見‘\n’
,這個我們通過觀察應該知道這是一個換行的標誌,相當於在末尾加了一個回車鍵。這就是轉義字元了,那在C語言中又有那些轉移字元呢,下面我列一張表給大家觀看觀看。
轉義字元 | 意義 |
---|---|
? | 在書寫連續多個問號時使用,防止它們被解析成三字母詞 |
\ ' | 用於表示字元常量 ‘ |
\ ‘’ | 用於表示一個字串內部的雙引號 |
\ \ | 用於表示一個反斜杆,防止它被解釋為一個轉移字元 |
\a | 警告字元,會發出蜂鳴聲 |
\b | 退格符號 |
\f | 進位制符號 |
\n | 換行 |
\r | 回車 |
\t | 水平製表符號 |
\v | 垂直製表符號 |
\ddd | ddd表示1~3個八進位制數字。如:\157X |
\xdd | dd表示2個十六進位制數字。 如:\xFF 1 |
#include <stdio.h>
int main()
{
printf("%c\n",'\'');
printf("%s\n", "\"\" ");
return 0;
}
會列印什麼呢?
這樣大家知道轉移字元的用法了嗎?下面我們再看一道題提示:strlen()
是計算字串長度(不計算‘\0’
哦)。
#include <stdio.h>
int main()
{
printf("%d\n", strlen("abcdef"));
// \32被解析成一個轉義字元 8進位制裡是不會有8的哦
printf("%d\n", strlen("c:\test\328\test.c"));
return 0;
}
大家答案是多少呢?6 18 還是6 14呢?
答案是6 18 的小夥伴算錯了哦。大家可以看上面的轉義字元表再來計算哦。
註釋
註釋大家應該一下子就能理解吧,就是用來解釋程式碼的意思或者是有些不需要的程式碼也可以註釋哦。
註釋用兩種風格:
-
C語言風格的註釋
/*xxxxxxx*/
- 缺陷是不能巢狀註釋
-
C++風格的註釋
//xxxxxxxx
- 可以註釋一行也可以註釋多行
選擇語句
選擇語句顧名思義就是用來選擇的語句。就好比,我們進入了學校,是好好學習呢?還是不學習呢?好好學習,畢業了就有好工作,不學習,畢業了就回家種田吧。這就是選擇。選擇語句也叫分支語句。
graph TD A[進入學校] --> C{好好學習?} C --> |好好學習| D[好工作] C --> |不學習| E[回家種田]用程式碼的形式表示:
#include <stdio.h>
int main()
{
int option = 0;
printf("你會好好學習嘛?(選擇 1 or 0):")
scanf("%d",&option);
if(1 == option)
{
printf("好好學習,你會有好工作\n");
}
else
{
printf("不學習,回家種田吧\n");
}
return 0;
}
還有其他形式嘛?會有的,在後續的部落格裡都會有,現在只是初步認識一下C語言。
迴圈語句
顧名思義就是迴圈的做某一件事情。比如我們選擇日復一日的學習。
C語言中如何實現迴圈呢?
- while語句
- for語句
- do...while語句
#include <stdio.h>
int main()
{
printf("加入學習\n");
int line = 0;
while(line <=30000)
{
line++;
printf("敲程式碼\n");
}
if(line>30000)
{
printf("成為高手\n");
}
return 0;
}
這裡是以while迴圈來舉個例子,後面的兩個迴圈會在後續文章講到。看一下執行的效果如何:
看到箭頭指向的滾動條,上面其實還有很多行資訊,這就是迴圈語句的效果,重複的做某一件事情。
函式
大家看到函式會想到什麼?我們學過數學中的函式。數學中的函式是怎麼樣的?f(x)=2*x+5。這就是數學中的函式。C語言中的函式呢?
函式是一段可以重複使用的程式碼,用來獨立地完成某個功能,它可以接收使用者傳遞的資料,也可以不接收。接收使用者資料的函式在定義時要指明引數,不接收使用者資料的不需要指明,根據這一點可以將函式分為有參函式和無參函式。
在C語言中函式是這樣定義的:函式的返回型別 函式名(函式引數型別 函式引數名){函式體,函式要實現的功能}
。我們把上面那段相加的程式碼改造一下:
#include <stdio.h>
int main()
{
int num1 = 0;
int num2 = 0;
int sum = 0;
printf("輸入兩個運算元:>");
scanf("%d %d", &a, &b);
sum = num1 + num2;
printf("sum = %d\n", sum);
return 0;
}
上述程式碼,寫成函式如下:
#include <stdio.h>
int Add(int x, int y)
{
int z = x+y;
return z;
}
int main()
{
int num1 = 0;
int num2 = 0;
int sum = 0;
printf("輸入兩個運算元:>");
scanf("%d %d", &num1, &num2);
sum = Add(num1, num2);
printf("sum = %d\n", sum);
return 0;
}
我們看實現的效果:
這是第一段程式碼的實現效果,下面我們看用函式的形式是怎麼樣的:
我們發現效果是一樣的,只是說相加的功能是在Add函式內部去實現了。因此我們知道函式的特點:
簡化程式碼,提高程式碼的複用性。
這只是自定義函式,在C語言中還有許多庫函式可以給我們使用,就是我們用到的printf 、scanf
等。
陣列
在介紹陣列之前,問一下大家,我們要定義1~10的數字,是怎樣定義呢?是一個一個的定義嘛?這樣會不會太麻煩了,其實也不是不行,那換作是100個或者是1000個呢?那我們這樣一個變數一個變數的定義實屬麻煩,所以我們要引用——陣列這個概念了。
陣列是一組相同型別元素的集合。就好比我要存整數1~10。
陣列定義
在C語言中是這樣定義的:型別名 陣列名[陣列元素個數]={初始值}
。
int arr[10]={1,2,3,4,5,6,7,8,9,10}
char arr[5]={'a','b','c'};
可以使用如上程式碼定義一個任何型別的陣列,當然除了那幾種基本資料型別哈。定義後我們可以對它進行初始化,也可不進行初始化,或者只初始化一部分就比如我上面建立的字元陣列。不初始化的話,陣列元素的值會被定義成隨機值的,所以一般在我們建立陣列後,我們都將它初始化為0,和定義變數一樣,這是一個好習慣哦。
陣列的使用
陣列都是通過下標來訪問的。上程式碼:
#include <stdio.h>
int main()
{
int i = 0;
int arr[10] = {1,2,3,4,5,6,7,8,9,10};//定義陣列並初始化
for(i = 0;i < 10;i++)
{
printf("%d ",arr[i]);//列印陣列內容
}
printf("\n");
return 0;
}
以上程式碼意思是定義了一個有十個整型大小的陣列並在定義時進行初始化,然後使用for迴圈將陣列元素打印出來。for迴圈後面會講。陣列的使用:通過陣列名+下標的方式進行引用,arr[i],其中[]
裡的數字就是我們的陣列下標。大家可以看到陣列下標是從0開始的,即想要獲取陣列第一個元素可以寫成:arr[0]
,因此我們知道陣列下標最大的是 陣列元素個數-1,第一個元素下標為0,以此類推。
操作符
指令系統的每一條指令都有一個操作符,它表示該指令應進行什麼性質的操作。操作符也就是不同的操作符有對應不同的操作嘛。
操作符的分類
<span id="“1"">算術操作符
操作符 | 作用 |
---|---|
+ | 加 |
- | 減 |
* | 乘 |
/ | 除 |
% | 取餘(取模) |
1.加、減、乘和平常數學中運算規則一樣。
2./(除)這個操作符需要注意一點:除的時候如果除號(/)兩邊只要有一個或者兩個浮點數,採用的是浮點型除法,若兩邊都是整型,則採用整數的除法,沒有餘數。
3.%這個操作符兩邊只能是整數,它返回的是整除之後的餘數。
算術操作符還算簡單,需要注意的地方我也標出來了。
移位操作符
操作符 | 作用 |
---|---|
<< | 左移操作符,二進位制序列左移 |
>> | 右移操作符,二進位制序列右移 |
- 移位操作符作用的物件是一個數的補碼;
- 對於左移:左邊拋棄,右邊補0;
- 對於右移:右邊拋棄,左邊補原來的符號位(採用算術移位);
- 對於’>>’操作符,分為兩種移位規則:一種邏輯移位,另一種是算術移位,
- a.邏輯移位規則是右邊拋棄,左邊最高位直接補0,不考慮之前數的符號位。
- b.算術移位規則是右邊拋棄,左邊最高位補和之前數相同的符號位。而目前編譯器通常採用的是算術移位規則。
- 但這裡需要注意,對一個數移位操作完成後,當前的數不會改變的,除非把它賦值給另外一個變數。
- 對於移位操作符,不要移動負數位,這個是標準未定義的。
上面提到了補碼,我要強調一個概念 原碼反碼補碼 (如果沒有基礎的,建議百度下)
並且一個整數4個位元組,由32位組成
其中正數的 原碼反碼補碼 相同
負數的 反碼等於原來相對應的正整數的原碼所有位按位取反.
負數的 補碼等於其反碼最低位 加 1
2>>1
00000000 00000000 00000000 00000010 //這是原碼 00000000 00000000 00000000 00000001 //這是右移1之後
2<<1
00000000 00000000 00000000 00000010 00000000 00000000 00000000 00000100 //這是左移之後
-1 << 1
10000000 00000000 00000000 00000001//這是-1的原碼 最高位 是符號位 11111111 11111111 11111111 11111110//這是-1的反碼 11111111 11111111 11111111 11111111//這是-1的補碼 11111111 11111111 11111111 11111110//這是-1左移1之後 這是補碼! 11111111 11111111 11111111 11111101//補碼-1 10000000 00000000 00000000 00000010//這是補碼還原之後 值為-2
-1 >>1
11111111 11111111 11111111 11111111//這是-1的補碼 11111111 11111111 11111111 11111111//這是-1右移1之後第一個1是加上去的 右邊丟棄左邊補補符號位。
int main()
{
int a=0,b=0,n=2;
a=n<<1;
printf("%d",a);
b=n>>1;
printf("%d",b);
}
觀察輸出的值可以得出結論:左移操作相當於給之前的數乘2,右移操作相當於給之前的數除2;
位操作符
位操作符針對的是二進位制位的。
操作符 | 作用 |
---|---|
& | 按位與 |
| | 按位或 |
^ | 按位異或 |
- 同樣這裡位操作符作用的物件也是一個數的補碼,並且它們的運算元必須是整數。
- 對於’&’,兩個數補碼對應位置的值都為1,結果為1,否則為0;對於’|’,兩個數補碼對應位置都是0,結果是0,否則為1;
賦值操作符
賦值操作符很簡單,顧名思義就是賦值用的。賦值操作符其實就是=
。但是其他的也叫複合賦值符。
操作符 | 作用 |
---|---|
= | 給一個變數賦值 |
+= | 相加之後賦值 |
-= | 相減之後賦值 |
*= | 相乘之後賦值 |
/= | 相除之後賦值 |
&= | 按位與之後賦值 |
^= | 按位異或之後賦值 |
|= | 按位或之後賦值 |
>>= | 右移之後賦值 |
<<= | 左移之後賦值 |
用複合操作符可以使程式碼更簡潔。
單目操作符
操作符 | 作用 |
---|---|
! | 邏輯取反操作 |
+ | 正值 |
- | 負值 |
& | 取地址 |
sizeof | 運算元的型別長度(以位元組為單位) |
~ | 對一個數的二進位制按位整體取反 |
-- | 前置、後置-- |
++ | 前置、後置++ |
* | 間接訪問操作符(解引用操作符) |
(型別) | 強制型別轉換 |
- sizeof操作符計算的是變數(型別)所佔空間的大小,是按位元組來計算,重要的是,sizeof (a+b)裡面的表示式不參與計算,若a,b都是int行型別,其結果依然是4;
- 當陣列作為引數為函式傳參時,由於陣列要發生降級,其會降級為一個指標,如果此時在一個函式中sizeof計算陣列的大小是不可以的,其計算的是陣列降級為指標的大小(4個位元組),所以,若函式要得到一個數組的大小,應該在主函式中計算。
- 對於前置++或 - -,先操作,再使用,對於後置++或 - -,先使用,再操作。
關係操作符
關係操作符就是用來判斷兩者之前的關係的。
操作符 | 作用 |
---|---|
> | 大於 |
>= | 大於等於 |
< | 小於 |
<= | 小於等於 |
!= | 不等於 |
== | 等於 |
這些關係操作符,比較簡單,不多說,但需要注意,不要將等於(==)寫成賦值操作(=)。
邏輯操作符
邏輯操作符用來判斷兩者之前邏輯關係的,與和或,想必大家高中有學過。
操作符 | 作用 |
---|---|
&& | 邏輯與 |
|| | 邏輯或 |
不同與按位與和按位或。
條件操作符
操作符 | 作用 |
---|---|
exp1?exp2:exp3 | exp1為真,結果為exp2,反之exp3 |
注:exp是表示式的意思 比如說 ( x > y ) ? x : y;
。
逗號表示式
操作符 | 作用 |
---|---|
exp1,epx2,....expN | 從左往後依次執行,整個表示式結果是最後一個表示式的結果 |
比如:int a = 3,b = 4;c = ( a > b, a = b * 4, b = a + 2);
結果是啥呢?我們來算算:
c = (3 > 4,a=4*4=16,b=16+2 ) 結果是18
下標引用操作、函式呼叫和結構成員
操作符 | 作用 |
---|---|
[] | 陣列下標操作符,運算元:陣列名+索引 |
() | 函式呼叫操作符 |
. | 訪問結構體成員 |
- > | 訪問結構體成員 |
用例子來解釋吧:
int arr[10]={1,2,3,4,5,6,7,8,9,10};
arr[2] = 3;
Add(a,b) 函式呼叫
.操作符
struct people
{
char name[10];//姓名
int age;//年齡
char sex[5];//性別
};
#include <stdio.h>
int main()
{
struct people x = {"許諾",20,"男"};
// .操作符訪問結構體成員
printf("姓名:%s\n",x.name);
printf("年齡:%d\n",x.age);
printf("性別:%s\n",x.sex);
return 0;
}
- >操作符
struct people
{
char name[10];//姓名
int age;//年齡
char sex[5];//性別
};
#include <stdio.h>
int main()
{
struct people x = {"許諾",20,"男"};
struct people n = &x;
// ->操作符訪問結構體成員
printf("姓名:%s\n",n->name);
printf("年齡:%d\n",n->age);
printf("性別:%s\n",n->sex);
return 0;
}
. ---> 結構體物件.成員名 -> ---> 結構體指標->成員名
常見關鍵字
關鍵字(Keywords)是由C語言規定的具有特定意義的字串,通常也稱為保留字,例如 int、char、long、float、unsigned 等。我們定義的識別符號不能與關鍵字相同,否則會出現錯誤。
你也可以將關鍵字理解為具有特殊含義的識別符號,它們已經被系統使用,我們不能再使用了。
標準C語言規定了32個關鍵字。
關鍵字 | 說明 |
---|---|
auto | 宣告自動變數 |
short | 宣告短整型變數或函式 |
int | 宣告整型變數或函式 |
long | 宣告長整型變數或函式 |
float | 宣告浮點型變數或函式 |
double | 宣告雙精度浮點型變數或函式 |
char | 宣告字元型變數或函式 |
struct | 宣告結構體變數或函式 |
union | 宣告共用資料型別(聯合體or共同體) |
enum | 宣告列舉型別 |
typedef | 用給資料型別取別名 |
const | 宣告只讀變數 |
unsigned | 宣告無符號型別變數或函式 |
signed | 宣告有符號型別變數或函式 |
extern | 宣告變數是在其他檔案正宣告 |
register | 宣告暫存器變數 |
static | 宣告靜態變數或函式 |
volatile | 說明變數在程式執行中可悲隱含地改變 |
void | 宣告函式無返回值或無引數,宣告無型別指標 |
if | 條件語句 |
else | 條件語句否定分支(與if連用) |
switch | 用於分支語句 |
case | 分支語句分支 |
for | 迴圈語句 |
do | 迴圈語句的迴圈體 |
while | 迴圈語句的迴圈條件 |
goto | 無條件跳轉語句 |
continue | 結束當前迴圈,開始下一輪迴圈 |
break | 跳出當前迴圈 |
default | 分支語句中“其他”分支 |
sizeof | 計算資料型別長度 |
return | 子程式返回語句(可以帶引數,也可以不帶引數)迴圈條件 |
這裡提一下幾個關鍵字:
typedef
typedef 型別定義,就是給已知的型別重新起個名字。
typedef unsigned int u_int;//給unsigned int 型別重新起了個名字叫 u_int 實際上還是unsigned int
int main()
{
unsigned int num1 = 10;
u_int num2 = 10;
//其實這兩個變數型別是相同的
return 0;
}
static
在C語言中:
static是用來修飾變數和函式的。
- 修飾區域性變數-靜態區域性變數
- 修飾全域性變數-靜態全域性變數
- 修飾函式-靜態函式
修飾區域性變數
#include <stdio.h>
//1
void test()
{
int a = 0;
a++;
printf("%d ",a);
}
//1 1 1 1 1
int main()
{
int i = 0;
while(i<5)
{
test();
i++;
}
return 0;
}
//2
void test()
{
static int a = 0;//static修飾區域性變數
a++;
printf("%d ",a);
}
//1 2 3 4 5
int main()
{
int i = 0;
while(i<5)
{
test();
i++;
}
return 0;
}
對比一下程式碼1和程式碼2的輸出結果,我們可以得出結論
static修飾區域性變數改變了變數的生命週期,讓靜態區域性變量出了作用域還存在不會自動銷燬,直到程式結束,生命週期才結束。
修飾全域性變數
這裡需要兩個原始檔來舉個例子:
//1
//test1.c
int a = 2021;
//test.c
int main()
{
extern int a;
printf("%d\n",a);
return 0;
}
//2
//test1.c
static int a = 2021;
//test.c
int main()
{
extern int a;
printf("%d\n",a);
return 0;
}
程式碼1效果:
可以看到程式正常執行。那我們看程式碼2效果:
可以看到程式報錯了,為啥呢?全域性變數,在其他原始檔內部可以被使用,是因為全域性變數具有外部連結屬性但是被static修飾之後,就變成了內部連結屬性,其他原始檔就不能連結到這個靜態的全域性變量了。
一個全域性變數被static修飾,使得這個全域性變數只能在本原始檔中使用,不能在其他原始檔中使用。
修飾函式
這也用兩個原始檔來舉例子:
//程式碼1
//test1.c
int Add(int x, int y)
{
return x+y;
}
//test.c
int main()
{
printf("%d\n", Add(2, 3));
return 0;
}
//程式碼2
//test1.c
static int Add(int x, int y)
{
return x+y;
}
//test.c
int main()
{
printf("%d\n", Add(2, 3));
return 0;
}
我們來看效果:
程式也是正常執行,那加上static修飾一下試試:
報錯了,跟上面修飾全域性變數有點相似。可以得出結論。
一個函式被static修飾,使得這個函式只能在本原始檔內使用,不能在其他原始檔內使用。
本質上: static是將函式的外部連結屬性變成了內部連結屬性! ( 和static修飾全域性變數一樣! )
#define 定義常量和巨集
之前常量有提到過#define
定義常量,大家應該也有點印象吧。 define其實是一個預處理指令。
define的用法:
define 定義符號 常量
#define MAX 1000
define 定義巨集
#define ADD(X,Y) ((X)+(Y))
我來解釋一下巨集是什麼,用官方一點的話來說就是:#define
機制包括了一個規定,允許把引數替換到文字中,這種實現通常稱為巨集(macro)或定義巨集(definemacro)。啥意思呢?咱們用程式碼來看看:
#include <stdio.h>
#define MAX 100
#define ADD(X,Y) ((X)+(Y))
int main()
{
int sum = ADD(10,20);
printf("sum = %d\n", sum);
sum = 10 * ADD(10,20);
printf("sum = %d\n", sum);
return 0;
}
大家看效果,是不是跟函式有點像,但又不是函式。之後深入瞭解,需要注意的是不要吝嗇括號這個東西哦,這個非常重要,有細心的有夥伴發現我把括號都給去掉了嘛?重這樣會造成問題的,比如說我把ADD()括號內部的值改一下,大家看效果:
結果是120????這是嘛肥事?不應該是300嗎???還記得之前說的注意點嗎?不要吝嗇括號哦,按照巨集的定義來解析:
int sum = 10 * ADD(10,20); int sum = 10 * 10 + 20;//先算乘除加算加減
懂了,在上面加上括號不就好了。嗯對,大家不要吝嗇括號哦。
???為啥還是120?我不是加了括號嘛?我們再來解析一下:
int sum = 10 * ADD(10,20); int sum = 10 * (10) + (20);
啊這....那要怎麼加括號呢?如果你想得到相加之後再乘以10是不是應該把這個相加的過程給括起來呢?
這樣就沒問題了。
指標
指標,沒學之前聽別人說過指標是C語言的靈魂,也是C語言的重點難點。簡單介紹一下指標是什麼吧,但是在學指標之前,我們要搞清楚一個東西,那就是記憶體。哈!記憶體,記憶體不就記憶體條嗎?NONONO,如果就只是個記憶體條的話那就不用學這玩意了。其實記憶體是電腦上特別重要的儲存器,計算機中所有程式的執行都是在記憶體中進行的。為了有效的管理記憶體空間,就把記憶體劃分成一個個小的記憶體單元,每個記憶體單元的大小是1個位元組。為了有效的訪問記憶體單元,就給記憶體編號,這些編號就被稱為該記憶體單元的地址。地址我們知道就比如我們住在亞洲中國34個省份的某一個城市某一個城區中。下面我畫個跟記憶體有關的圖,我假設我畫的的是記憶體:
上面我寫了一個位元位作為一個地址太浪費了而且管理起來也不方便,之前介紹過基本型別發現char型別是最小的而且大小隻有一個位元組,那麼一個位元組作為記憶體單元的大小這樣不就好了嗎?確實C語言規定記憶體單元的大小就是1個位元組。大家多多少少都對記憶體有大概的瞭解了吧。接著我們說指標。
在電腦科學中,指標( Pointer )是程式語言中的一個物件,利用地址,它的值直接指向( points to )存在電腦儲存器中另一個地方的值。由於通過地址能找到所需的變數單元,可以說,地址指向該變數單元。因此,將地址形象化的稱為“指標"。意思是通過它能找到以它為地址的記憶體單元。
變數都會有地址,簡單的來說,指標就是一個變數,而這個變數只是用來儲存地址的。下面以程式碼為例子:
int main()
{
int num = 10;
#//取出num的地址
printf("%p\n", &num);//列印地址,%p--以地址的形式列印
return 0;
}
指標就是用來儲存地址的,而且指標也是個變數,那麼就可以定義指標變數來儲存地址:
#include <stdio.h>
int main()
{
int num = 10;
int* p = #
printf("%p\n",p);
*p = 20;
printf("%d\n",num);
return 0;
}
我們看結果,第一個是00AFF9D8,第二個是20,咦我的num怎麼被改變了呢?第一個是地址是因為指標變數存的是num的地址,因為p變數指向的是num,也就是p變數通過地址找到了num,*
解引用操作符,間接訪問了num,*p
找到的就是num,這樣num的值就被改變了。這只是整型指標,我們可以以此類推到其他型別,比如:
#include <stdio.h>
int main()
{
char ch = 'w';
char* p = &ch;
*p = 'q';
printf("%c\n",ch);
return 0;
}
畫個圖大家應該可以更清楚一點:
p的值是地址哦,比如int* p = 20
;它會被解析成:
int * p = 20 == 0x00000014;//20會被解析成地址 而地址是十六進位制的
可以看到int* p = 某個值
,某個值都會被解析成地址的形式。大家要注意哦!
指標的大小
我們知道了指標是個用來存放地址的變數,那指標變數的大小是多少呢?
#include <stdio.h>
int main()
{
printf("int* = %d\n",sizeof(int*));
printf("char* = %d\n",sizeof(char*));
printf("short* = %d\n",sizeof(short*));
printf("float* = %d\n",sizeof(float*));
printf("boule* = %d\n",sizeof(double*));
return 0
}
我們在不同的平臺上執行一下,32位:
我們發現在32位平臺上,所有型別指標都是4位元組。
64位:
我們看到在64位平臺上都是8個位元組。我們可以得出結論:
指標大小在32位平臺上是4個位元組,64位平臺上是8個位元組。
結構體
我們在介紹一本書的時候會怎樣描述呢?書名+價格+書號+出版社等等。對吧,或者描述一個學生,學生有姓名、年齡、性別、學號等。這些在C語言中又是怎樣描述的呢?這就牽扯到了C語言中的結構體了,結構體可以讓C語言有能力描述這些複雜的型別。那我們用結構體來描述一個學生型別:
struct Student
{
char name[10];//名字
int age; //年齡
char sex[5]; //性別
char id[19]; //學號
};
結構體的定義方式:
struct 結構體名稱
{
型別 結構體成員1;
型別 結構體成員2;
型別 結構體成員3;
.....
};
初始化並定義結構體變數方式位:
struct 結構體名稱 結構體變數名 = {成員1賦值,成員2賦值,.....};
#include <stdio.h>
//定義一個結構體型別 結構體名稱Student
struct Student
{
char name[10];//名字
int age; //年齡
char sex[5]; //性別
char id[19]; //學號
};
int main()
{
//定義一個結構體Student型別的變數 並初始化
struct Student s = {"小顧",20,"男","201717101"};
printf("姓名:%s\n",s.name);
printf("年齡:%d\n",s.age);
printf("性別:%s\n",s.sex);
printf("學號:%s\n",s.id);
return 0;
}
也可以使用 - >
操作符來訪問哦:
#include <stdio.h>
//定義一個結構體型別 結構體名稱Student
struct Student
{
char name[10];//名字
int age; //年齡
char sex[5]; //性別
char id[19]; //學號
};
int main()
{
//定義一個結構體Student型別的變數 並初始化
struct Student s = {"小顧",20,"男","201717101"};
struct Student* p = &s;
printf("姓名:%s\n",s->name);
printf("年齡:%d\n",s->age);
printf("性別:%s\n",s->sex);
printf("學號:%s\n",s->id);
return 0;
}
效果跟上圖是一樣的大家可以試試。
同樣的跟修改變數類似,也可以對結構體變數中的值操作,改變結構體成員的值。
#include <stdio.h>
//定義一個結構體型別 結構體名稱Student
struct Student
{
char name[10];//名字
int age; //年齡
char sex[5]; //性別
char id[19]; //學號
};
int main()
{
//定義一個結構體Student型別的變數 並初始化
struct Student s = {"小顧",20,"男","201717101"};
struct Student* p = &s;
printf("%-20s\t%-20s\t%-20s\t%-20s\n","姓名","年齡","性別","學號");
printf("%-20s\t%-20d\t%-20s\t%-20s\n", s.name, s.age, s.sex, s.id);
printf("%-20s\t%-20d\t%-20s\t%-20s\n", p->name, p->age, p->sex, p->id);
s.name = "小明";
printf("修改後的姓名:%s\n",s.name);
p->age = 19;
printf("修改後的年齡:%d\n", p->age);
return 0;
}
程式碼效果圖:
大家可以看到效果正如我們所改動的一樣。但是呢,為啥沒有改變名字呀?因為姓名是字串陣列是不可以直接改變的,得用字串函式來改變,給大家演示一下吧,既然需要字串函式,那就需要引入相對應的標頭檔案string.h
,字串函式其中的strcpy
函式,用來拷貝字串的。
有關於字串操作函式,放到後面再說。
感謝大家的觀看!謝謝大家!!!本人也只是菜鳥一枚,有哪些寫的不好的地方,講的不明白的地方大家都可以指點指點,謝謝大家!本人也沒學的好,所以也難免有些地方沒有寫好,還望大家體諒體諒,謝謝大家!本人寫這篇部落格也是為了分享一下自己的理解,有一起學習的小夥伴可以互相支援一下哦!這篇也只是初步的認識一下C語言,認識C語言都有哪些內容。好了謝謝大家的觀看!!!阿里嘎多擴散一碼事!!!
</stdio.h></stdio.h></stdio.h></stdio.h></stdio.h></stdio.h></stdio.h></stdio.h></stdio.h></stdio.h></stdio.h></stdio.h></stdio.h></stdio.h></stdio.h></stdio.h></stdio.h></stdio.h></stdio.h></stdio.h></stdio.h></stdio.h></stdio.h></stdio.h></stdio.h>