學前準備
- 誤區1:現在很多人都認爲C++、Python以及JAVA等語言比較流行,C語言不如他們,這其實是不正確地理解。先看一下2019年各大編程語言TIOBE排行。其中用C語言編寫的相關軟件中佔16%左右,居於第二。JAVA由於是編寫網站以及手機APP的語言,所以應用比較廣泛。但是C語言可以稱作標準模範語言,其他語言也是模仿C語言衍生出來的,可以叫做C-Like語言。
- 誤區2:C語言是編寫操作系統以及嵌入式軟件系統的必要語言,其他語言無法擔任此項重任。C++也是由C語言改進衍生出來的,所以C++向下兼容C語言,即在C++中可以寫C語言語法混編。C++的一些特性和功能比較友好(如標準STL庫),但C++語言也是經過封裝後的語言,某些地方並不如C語言運算效率高,所以最好的方式是混編,這也可能存在某些語法兼容的問題。
- 如何選擇編程語言:語言肯定支持多種,C、C++、JAVA等,基本上都在使用C或C++,像JAVA、Python等語言可能會超時,在知乎上看到一些用JAVA超時的都轉爲C/C++了,所以推薦C/C++爲妙。
- 如何選擇編譯器:由於VC6.0過於古老,很多語法在編譯時不會通過,不要使用。一般推薦使用Dev-C++,比較好用;VS的會是第二選擇吧,在VS中使用C語言的printf和scanf輸入輸出時會報不安全語法錯誤,需要改爲printf_s、scanf_s使用,語法不能完全兼容;Eclipse是JAVA的編輯器,最終推薦Dev-C++。
基本數據類型
- 變量定義:
變量類型 變量名 = 初值;
int a = 0;
- 變量名規則:
1)不能是C語言保留字(關鍵字 ),如:auto,break,case,char,const,continue,default,do,double,else,extern,if,for,while等。
2)變量名由字母,數字以及下劃線組成,數字不能在變量名的第一個位置。
3)區分大小寫,tju和Tju是 兩個不同的變量名。
4)變量名建議取有實際意義的變量名,提高程序的可讀性。 - 變量類型:整型(int和long long),浮點型(float和double),字符型char,布爾型bool。
- 強制類型轉換:
格式:(新類型名)變量名
double r = 12.35;
printf("%d\n",(int)r);
- 宏定義常量和const常量
//宏定義,在main函數之前定義,不加分號,定義的標誌符pi在全局值都爲3.14
#define pi 3.14
//const常量,同宏定義一樣,一旦定義好,其值就無法改變。
//一般定義常量值推薦const定義。
const double pi = 3.14;
- 宏定義函數
#include <stdio.h>
#define ADD(a,b) (a+b)
int main()
{
printf("%d\n",ADD(3,2));
return 0;
}
- 除法運算符和取模運算符
#include <stdio.h>
int main()
{
int a=5,b=4;
float c = 5.0;
double d = 6.0;
printf("%d %f %d",a/b,c/b,a%b);
return 0;
}
運行結果:
1 1.250000 1
- 自增運算符(與自減運算符相同)
記住:i++是先使用i在加1,++i是先加1在使用i
#include <stdio.h>
int main()
{
int a=1,b=1,n1,n2;
n1 = a++;
n2 = ++b;
printf("%d %d\n",n1,a);
printf("%d %d\n",n2,b);
}
運算結果:
1 2
2 2
- 條件運算符(三目運算符)
格式 A?B:C
其含義是,如果A爲真則執行並返回B的結果,A爲假則執行並返回C的結果。
#include <stdio.h>
int main()
{
int a=3,b=5;
int c = a>b?7:11;
printf("%d\n",c);
return 0;
}
運算結果:
11
- 位運算符(針對二進制操作)
下面的兩種定義是等價的。
const int INF = (1<<30)-1;
const int INF = 0x3fffffff;
順序結構
- scanf和printf輸入/輸出
int n = 0;
scanf("%d",&n);
注意:&爲取地址運算符,在變量定義之後,就會在計算機內存中分配一塊空間給這個變量,除了char數組不加&之外,其他變量類型在scanf中都要加上取地址運算符&。char數組本身就代表這個數組第一個元素的地址,所以不需要加&。
數據類型 | 格式符 | 舉 例 |
---|---|---|
int | %d | scanf("%d",&n) |
float | %f | scanf("%f",&n) |
double | %lf | scanf("%lf",&n) |
long long | %lld | scanf("%lld",&n) |
char | %c | scanf("%c",&n) |
字符串(char數組) | %s | scanf("%s",str) |
記住:字符數組使用%s格式讀入時以空格跟換行爲讀入結束的標誌。
#include <stdio.h>
int main()
{
char str[10];
scanf("%s",str);
printf("%s",str);
return 0;
}
//輸入數據
abcd efgh
//輸出數據
abcd
記住:printf中的float和double輸出格式都是%f,float精度比較低,書中推薦使用double類型;下面例子中兩個float比較大的數相乘,得到的數在整數位就已經不準了,所以在有效位超過7位的數選擇double類型計算。
#include <stdio.h>
int main()
{
float f1 = 8765.4,f2 = 8765.4;
double d1 = 8765.4,d2 = 8765.4;
printf("%f\n%f\n",f1*f2,d1*d2);
return 0;
}
輸出結果:
76832244.007969
76832237.160000
- typedef
它的作用是給一個複雜的數據類型起一個別名,提高編碼效率。
#include <stdio.h>
typedef long long LL;
int main()
{
LL a = 123456789012345LL, b = 234567890123456LL;
return 0;
}
- 常用math函數
注意:頭文件加入math.h
1)絕對值函數:fabs(double x)
2)向下取整函數:floor(double x)
3)向上取整函數:ceil(double x)
4)開方函數:pow(double x,double y)
5)算數平方根:sqrt(double x)
6)四捨五入:round(double x) - break和continue語句
break一般在選擇結構中經常遇到,比如switch順序語句中case的跳出,for以及while循環語句中的跳出。
//break示例
#include <stdio.h>
int main()
{
int n,sum = 0;
for(int i = 1;i<=100;i++)
{
sum += i;
if(sum>=2000) break;
}
return 0;
}
//continue示例
#include <stdio.h>
int main()
{
int sum =0;
for(int i = 1;i<=5;i++)
{
if(i%2==1) continue;
sum += i;
}
printf("sum = %d\n",sum);
return 0;
}
輸出結果:sum = 6
數組
數組是把相同數據類型的變量組合在一起產生的數據集合(容器),數組的每個變量地址是連續的。
注意:C99標準之前,數組大小必須是整數常量,不可以是變量;C99之後數組大小可以是變量。
- 數組賦初值0有三種形式,推薦使用memset函數,速度快:
1)int a[10] = {0};
2)int a[10] = {};
3) int a[5] = {};
memset(a,0,sizeof(a));
- 冒泡排序(經典)
核心在於交換,比如從小到大排列,比較相鄰的兩個數,將大數向右移動,直至在最右側,依次執行,比如將數組從小到大進行冒泡排序。
#include <stdio.h>
int main()
{
int a[10] = {3,1,4,5,2};
for(int i = 0;i < 5 - 1;i++)
{
for(int j=0;j<5-i-1;j++)
{
if(a[j]>a[j+1])
{
int temp = a[j];
a[j] = a[j+1];
a[j+1] = temp;
}
}
}
for(int i= 0;i<5;i++)
{
printf("%d ",a[i]);
}
return 0;
}
- 堆棧的理解
1)堆和棧都是存放臨時數據的,棧只能在棧頂操作,所以先進後出,後進先出,堆沒有先後特性。
2)堆是程序運行時請求操作系統分配 給的內存,一般是C/C++分別使用malloc/New來申請分配內存,使用free/delete來釋放申請的內存,由於操作系統OS在分配和銷燬內存都需要佔用時間,所以使用堆的效率比較低,最起碼比棧低很多;堆的好處是可以分配很大的內存。
3)程序的局部變量存在棧中,全局變量存在在靜態區,動態內存申請存在於對中。
4)static修飾的變量存在於靜態存儲區,不管在函數內還是函數外只進行一次 初始化。
5)全局變量與靜態全局變量區別:作用於不同,全局變量可以再其他文件中訪問,而靜態全局變量只能在本文件訪問。
5)局部變量與靜態局部變量區別:局部變量在函數內,存在於棧中,調用函數時動態創建,函數執行完則動態銷燬;靜態局部變量存在於靜態存儲區 ,只初始化一次。 - string.h頭文件函數
1)strlen()得到字符串中字符個數
2)strcpy(str1,str2)將字符串str2複製給str1
3)strcat(str1,str2)將字符串str2拼接在str1後面
函數
- 以數組作爲函數參數
數組可以作爲形參傳入,但注意的是:數組第一位不寫長度,第二維需要寫長度,實際調用時也需要寫數組名;數組爲形參時,對數組元素的修改就等於對原數組進行修改,這與普通局部變量不同,實例如下:
(1)普通局部參數
#include <stdio.h>
void change(int x)
{
x = x + 2;
printf("%d\n",x);
}
int main()
{
int x = 1;
change(x);
printf("%d\n",x);
return 0;
}
運行結果:
3
1
(2)數組參數
#include <stdio.h>
void change(int a[],int b[][5])
{
a[0] = 1;
a[1] = 3;
a[2] = 5;
b[0][0] = 1;
}
int main()
{
int a[3] = {0};
int b[5][5] = {0};
change(a,b);
for(int i=0;i<3;i++)
{
printf("%d\n",a[i]);
}
return 0;
}
運行結果:
1
3
5
- 函數遞歸
函數遞歸是指一個函數調用該函數本身,實例:計算n的階乘。
#include <stdio.h>
int f(int n)
{
if(n == 0) return 1;
else return f(n-1)*n;
}
int main()
{
int n = 6;
printf("%d\n",f(n));
return 0;
}
運行結果:
720
指針
- 什麼是指針
變量的地址一般指它佔用的字節中第一個字節的地址,C語言的指針指向了變量的內存地址,所以說指針是一個unsigned類型的整數。 - 如何取得一個普通類型變量地址:取地址運算符&
#include <stdio.h>
int main()
{
int a = 1;
printf("%d,%d\n",&a,a);
return 0;
}
運算結果:
648728,1
- 指針變量
注意:“*”位置可以在數據類型之後或者變量名之前,一般程序員習慣把星號放在數據類型之後。
int* p;
double* p;
char* p;
當存在多個同類型指針變量時如下定義:
//三個都是指針變量
int *p1,*p2,*p3;
//只有第一個p1是指針變量,p2、p3是int類型
int* p1,p2,p3;
- 指針變量:
//三個都是指針變量
#include <stdio.h>
int main()
{
int a;
int* p = &a;
*p = 233;
//
printf("%d,%d,%d,%d\n",*p,a,p,&a);
return 0;
}
輸出結果:
233,233,6487620,6487620
- 使用指針變量作爲函數參數
1)不使用指針函數參數
函數在接收參數時是單向一次性的值傳遞,所以在調用swap函數時只是把a,b的 值傳進去了。
#include <stdio.h>
void swap(int a,int b)
{
int temp = a;
a = b;
b = temp;
}
int main()
{
int a=1,b=2;
swap(a,b);
printf("a = %d,b = %d\n",a,b);
return 0;
}
輸出結果:
a = 1,b = 2
2)使用指針函數參數
#include <stdio.h>
void swap(int* a,int* b)
{
int temp = *a;
*a = *b;
*b = temp;
}
int main()
{
int a=1,b=2;
int *p1 = &a,*p2 = &b;
swap(p1,p2);
printf("a = %d,b = %d\n",a,b);
return 0;
}
輸出結果:
a = 2,b = 1
- 引用
引用的&與取地址的&不同,C++中的改變傳入函數參數的方法有指針傳參和引用傳參。
#include <stdio.h>
void change(int &x)
{
x = 2;
}
int main()
{
int a = 1;
change(a);
printf("%d\n",a);
return 0;
}
輸出結果:
2
- 結構體
#include <stdio.h>
struct Point
{
int x,y;
//不經過初始化定義構造函數
Point(){}
//提供x,y的初始化構造函數
Point(int _x,int _y):x(_x),y(_y){}
}pt[10];
int main()
{
int num = 0;
for(int i = 0;i<=3;i++)
{
for(int j=0;j<=3;j++)
{
pt[num++]= Point(i,j);
}
}
for(int i=0;i<num;i++)
{
printf("%d,%d\n",pt[i].x,pt[i].y);
}
return 0;
}
運行結果:
0,0
0,1
0,2
0,3
1,0
1,1
1,2
1,3
2,0
2,1
2,2
2,3
3,0
3,1
3,2
3,3
- cin與cout
C++的輸入輸出函數,不需要像C語言scanf與printf那樣指定輸入輸出格式,與不需要取地址運算符&,比較簡便。
- 複雜度
1)時間複雜度:時間複雜度是算法需要執行基本運算的次數所處的等級,類似於加減乘除計算機可以直接實現的運算。時間複雜度是評判算法時間效率的有效標準。
2)空間複雜度:表示算法需要消耗的最大數據空間。
3)編碼複雜度:沒有量化標準。