初識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
- 可以注釋一行也可以注釋多行
選擇語句
選擇語句顧名思義就是用來選擇的語句。就好比,我們進入了學校,是好好學習呢?還是不學習呢?好好學習,畢業了就有好工作,不學習,畢業了就回家種田吧。這就是選擇。選擇語句也叫分支語句。
用代碼的形式表示:
#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語言都有哪些內容。好了謝謝大家的觀看!!!阿里嘎多擴散一碼事!!!