C語言入門

C語言須知和開始

初識C語言

1987年布萊恩柯林漢和丹尼斯里奇合做的C語言程序設計初版是公認的C標準:html

  • 第一個ANSI/ISO C標準
    一般叫作C89(由於ANSI與1989年批准該標準)或C90(由於ISO與1990奶奶批准該標準),因爲ANSI先公佈C標準,所以業界人士一般使用ANSI C。
    該委員會指定的指導原則中,最有趣的多是:保持C的精神。委員會在表述這一精神時列出瞭如下幾點:
    • 信任程序員
    • 不要妨礙程序員作須要作的事
    • 保持語言精練簡單
    • 只提供一種方法執行一項操做
    • 讓程序運行更快,即便不能保證其可移植性
  • C99標準 1994年,ANSI/ISO聯合委員會開始修訂C標準,最終發佈了C99標準。
    • 支持國際化編程 例如:提供多種方法處理國際字符集
    • 調整現有實現致力於解決明顯缺陷 例如:須要將C移至64位處理器
    • 爲適應和工程項目中的關鍵數值計算,提升C的適應性。
  • C11標準 標準委員會在2007年承諾C的標準下一個版本是C1X,2011年終於發佈了C11標準。這次,委員會提出了一些新的指導原則。處於對當前編程安全的擔心,不那麼強調「信任程序員」目標了。並且,供應商並未像對C90nayang很好地接受和支持C99。這使得C99的一些特性稱爲C11的可選項。由於委員會認爲,不該要求服務小型機市場的供應商支持其目標環境中用不到的特性。另外須要強調的是,修訂標準的緣由不是由於原標準不能用,而是須要跟進新的技術,好比,新標準添加了可選項支持當前使用多處理器的計算機。對於C11標準,咱們淺嘗輒止。

開發工具

在Windows軟件開發中,Microsoft Visual Studio及其免費版本Microsoft Visual Studio Express 都久負盛名,它們與C標準的關係也很重要。然而,微軟鼓勵程序員從C轉向C++和 C#。雖然Visual Studio 支持C89/C90,可是到目前爲止,它只選擇性的支持那些在C++新特性中能找到的C標準(如,long long 類型)。並且,自2012版本起,Visual Studio 再也不把C做爲項目類型的選項。儘管如此,本書中的絕大多數程序仍可用Visual Studio來編譯。java

  • Visual Sutdio 32位如下面的方式
    在新建項目時,選擇C++選項,而後選擇【Win32 控制檯應用程序】,在應用設置中選擇【空項目】。幾乎全部的C程序都能與C++程序兼容。因此,本書中的絕大多數C程序均可做爲C++程序運行。或者,在選擇C++選項後,將默認的源文件擴展名.cpp替換爲.C,編譯器便會使用C語言的規則代替C++。
  • Visual Studio 64位 如下面的方式:
  1. 在新建項目時,選擇C++選項,而後選擇Windows桌面嚮導,點擊確認-項目新建成功,其餘選項裏面選擇空項目-點擊確認,在源文件新建一個項,選擇C++文件,把.cpp更改成.c。 而後就能夠運行了
    這裏須要注意的是 一個擁有main()方法的.c文件就是一個獨立的項目,因此須要運行更多的main()方法,那麼只有一種辦法就是點擊解決方案第一個右鍵繼續新建一個項目(循環上一步),而後編碼,編碼完成後,點擊該項目右鍵,設置爲啓動項目。
  2. 在新建項目時,選擇C++選項,而後選擇Windows控制檯應用程序,點擊確認-項目新建成功,在源文件裏面有個 xxx(項目名).cpp 更改成.c,報錯的代碼刪除,就能夠直接運行了。

C 語言語法

基本要求和變動

int main(void)  void main()	void main(String[] args) 也能夠像java同樣這樣. 
/* 雙斜線 單行註釋 是C99標準之後加的
C90,要求吧變量聲明在塊的頂部,其餘語句不能任何聲明的前面。 C99和C11 改變了這個的限制,能夠隨意聲明。
C99 和C11 容許使用更長的標識符名(變量名),可是編譯器只識別前63個字符。C90只容許6個字符,舊式編譯器一般最多隻容許8個字符。
變量名能夠用 大、小寫字母和下劃線來命名。第一個字符必須是字符或下劃線,不能是數字。
*/
複製代碼

數據類型和使用

基本數據類型和使用
  • 日常基本使用就使用 int long short unsigned char float double 就完了, 因此只須要有哪些東西存在就好了,真的在使用比較大的數作運算處理的時候, 再去使用它們.
數據類型 printf
int %d
short %d 或者 %hd
long %d
float %d 或者 %hd
double %d
char %d 或者 %hd
16進制 %x
8進制 %o
字符串 %s
16進制long 類型 %lx
8進制打印long 類型 %lo
字符串 %s
指數基數法的浮點數 %e (若是系統支持16進制格式的浮點數),可用a和A分別代替e和E
short int %d
long int %ld (在32位環境中實際上是同樣的long int 和long)
long long %lld
long long int %lld
unsigned long %lu
int32_t(是int類型的別名,是精確寬度整數類型,int32_t表示的寬度正好爲是32位的時候,可是計算機底層系統不支持。所以這是個可選項。若是系統不支持精確寬度整數類型怎麼辦?C99和C11提供了第2類別名集合:) %" PRId32 "
int_least8_t (至少是8位有符號的整數值的一個別名) %" PRId32 "
int_fast8_t (至少8位有符號值而言運算最快的整數類型別名) %" PRId32 "
intmax_t (C99定義了 intmax_t (最大的有符號整數) 相似的還有 unitmax_t 表示最大無符號整數類型。 這些類型可能比 long long 和 unsigned long 類型更大。) %" PRId32 "
int32_t %" PRId32 "(上面的這些可移植類型,在printf 打印的時候 有些使用 %d,有些使用 %ld ,C標準針對這一狀況提供了 RPId32 \ 64 字符串宏來顯示可移植類型。 還有 PR (Id li lo lu ) 不少種 宏)
//stdudio.h 至關於java 的lang包,在C中studio.h 是C語言的一個標準庫, .h表明只有函數的聲明,沒有函數的實現,由於C語言是系統底層的語言,因此他的這些函數,都在window,linux 有不一樣的實現,在編譯器把.c 或者C++的.cpp文件編譯完成後,執行的時候咱們調用的這些聲明函數就會去系統底層找本身的實現. 
#includeo <studio.h> //include 預處理指令,相似java 的import
void main(){
	printf("n = %d, n squared = %ld, n cubed = %f\n", int, long,float);
}



/* char 類型: char 類型存儲的一個字符,可是從技術層面看,char是整數類型。由於char類型實際上存儲的是整數而不是字符,計算機使用數字編碼來處理字符,即用特定整數表示特定的字符,參考ASCII編碼表。 轉義序列: // \a 警報 // \b 退格 // \f 換頁 // \n 換行 // \r 回車 // \t 水平製表符 // \v 垂直製表符 // \\ 反斜槓(\) // \' 單引號 // \" 雙引號 // \? 問號 // \0oo 八進制(oo必須是有效的八進制數,即每一個o可表示0~7中的一個數) // \xhh 十六進制(hh必須是有效的十六進制數,即每一個h可表示0~f中的一個數) */

複製代碼
指數記數法(插曲)

由於上面有個指數記數,學習了就記個筆記.linux

數字 科學計數法 指數記數法
100000000 1 X 10(9次方) 1.0e9
322.56 3.2256 X 10(2次方) 3.2256e2
0.000056 5.6 X 10(-5次方) 5.6e-5
C標準規定,float類型必須至少能表示 10(-37次方) ~ 10 (37次方), float 至少精確到小數點後6位。
// double類型和float類型的最小取值範圍相同,可是至少必須能表示10位有效數字。 (經驗: 無論系統差別給小數部分分多少,通常狀況下,double至少有13位小數精度)
// C99 標準添加了一種新的浮點型常量格式--用十六進制表示浮點型常量。十六進制前加(0x或0X),用p 或P 分別表明e或E, 例如: 0xa.1fp10 ,打印16進制浮點型 使用 %e或%le
// 使用C語言,不少公司都有系統化的命名規範,好比 int i_smart; unsigned short us_versmart,一看命名就知道是什麼類型
// 複數和虛數:
複製代碼
定義常量
#define DENSITY 62.4 // 定義一個常量

#include <limits.h>
#include <float.h>
void main() {
	char name[40];
	/* 定義常量: #define DENSITY 62.4 這樣像include 同樣定義的常量,程序編譯時,程序中的全部DENSITY 都被替換成了62.4,這個過程稱爲編譯時替換,一般這種常量也叫作明示常量 C90 標準新增 const 關鍵字, const int MONTHS = 12; 用於限定一個變量爲只讀. C頭文件 limits.h 和 float.h , C 語言 char類型數組必須以\0(ASCII對應0) 結束,那麼40個就只能存儲39個字符. 'x' 和"x" 就不一樣 一個是char基本類型,一個是char數組類型(後面就是\0) sizeof(name) = 40; strlen(name) = 實際存儲的字符長度; */
}
複製代碼
指針
  • 指針是什麼?
    指針是一個變量,來存儲int char ..... 用來存儲各類類型定義的變量的內存地址. 並且指針還須要有類型.
  • 指針還須要有類型?
    那麼在C中指針爲何要有類型,由於指針存儲的是一個變量的內存地址,那麼若是有人要使用指針去拿取內存地址的數據的時候,該怎麼去拿呢,即便拿出來又該怎麼斷定它是什麼類型的呢.
    指針有類型,地址沒有類型.,地址只是開始的位置,類型讀取到上面位置結束,(例如:若是你是int類型 加入int佔4字節,那麼指針的類型就告訴你讀到哪裏結束. 內存地址告訴你從內存哪一個位置開始讀.)
  • 使用 & 去地址符 (int i = 89; int *p = &i); 定義一個變量 i = 89, &i 拿到 i 變量的內存地址,賦值給 int *p 一個int 類型的指針變量.
int main(void) {
  int i = 89;
  int *p = &i; //
  double j = 78.9;
  //賦值給double 類型變量的地址
  p = &j;
  printf("double size %d\n",sizeof(double));
  printf("%#x,%lf\n",p,*p);
}
複製代碼
  • 空指針 空指針的默認地址爲0,訪問內存地址0,操做系統不容許.
void main() {
	int i = 9;
	int *p = NULL;
	//p = &i;
	printf("%d\n", *p); //空指針的默認地址爲0,訪問內存地址0,操做系統不容許.
	getchar();
}
複製代碼
  • 二級指針 和多級指針 JNIEnv 是 jni.h 中 _JNIEnv 就是一個JNINativeXXX* 就是一個指針, 多級指針(二級指針),指針保存的是變量的地址,保存的變量還能夠是一個指針變量 動態內存分配 二維數組,都須要用的到多級指針
void main() {
	int a = 50;

	int* p1 = &a;

	int** p2 = &p1;

	int*** p3 = &p2;
	printf("p1 : %#x, p2 : %#x", p1, p2);


	int* qP1 = *p3;
	int* qP2 = *p2;
	int qa = *qP2;


	//等同於
	int qa1 = ***p3;
	printf("qa %d",qa);
	//printf("p2 %#x",);
	getchar();
}
複製代碼
  • 指針的運算
void main() {
	//int ids[] = {78, 90, 23, 65, 19};
	////數組變量名ids就是數組的首地址,下面這三個打印出來的地址是同樣的
	//printf("%#x \n", ids); 
	//printf("%#x \n", &ids);
	//printf("%#x \n", &ids[0]);

	////指針變量
	//int *p = ids;
	//printf("%d \n", *p);
	////指針的加法, +1 向前移動sizeof(數據類型)個字節, 至關於直接移動到數組的下一個index 的地址.
	///*數組在內存中是連續存儲的,
	//*/
	//p++;

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



	//高級寫法
	int uids[5];
	//int i = 0;
	//for (int i = 0; i < 5; i++)
	//{
	// uids[i] = i;
	//}
	//早些版本的寫法,p<uids+5 uids是0 index 的內存地址,+5 index-5 後面的那個內存地址,p初始值就是uids,那麼p++ 直到循環完畢.
	int* p = uids;
	printf("%#x\n",p);
	int i= 0;
	for (;p< uids+5; p++)
	{
		*p = i;
		i++;
	}
	
	getchar();
}
複製代碼
  • 函數指針
void msg(char* msg, char* title) {
	MessageBox(0, msg,title,0);
}

//函數指針
void main() {
	//msg();
	printf("%#x msg\n",msg);
	printf("%#x msg\n", &msg);
	//函數指針 void函數返回值類型 (*fun_p)函數指針的名稱,() 函數的參數列表
	void(*fun_p)() = msg;
	fun_p("xiaoxi內容","biaoti");

	getchar();
}
複製代碼
  • 練習 (用隨機數生成一個數組,寫一個函數查找最小值,並返回最小數的地址,在主函數總打印出來)
int* getMinPointer(int ids[], int len) {
	int i = 0;
	int* p = &ids[0];
	for (;  i< len; i++)
	{
		if (ids[i] < *p) {
			p = &ids[i];
		}
	}
	return p;
}


void main() {
	int ids[10];
	int i = 0;
	//初始化隨機數發生器,設置種子,種子不同,隨機數纔不同
	//當前時間做爲種子
	srand((unsigned)time(NULL));
	for (int i = 0; i < 10; i++)
	{	
	
		ids[i] = rand() % 100;//生成隨機數 rand不能生成 必須指定種子 模100就是100之內的數.
		printf("%d ",ids[i]);

	}
	int* p_1 = getMinPointer(ids, sizeof(ids) / sizeof(int));
	printf("%#x, %d \n",p_1,*p_1);
	getchar();
}
複製代碼
動態內存

爲何要用動態內存:
靜態內存分配,分配內存大小是固定的,問題: 1. 很容易超出棧內存最大值 2.爲了防止內存不夠用會開闢更多的內存 動態內存分配,在程序運行過程當中,動態指定須要使用的內存大小,手動釋放,釋放以後這些內存還能夠供其餘程序使用或從新使用.c++

  • 動態內存分配
void main() {
	//40M
	//stack overflow 錯誤,棧溢出
	//int a[1024 * 1024 * 10]; //在棧區直接分配40M內存,正常來講window下面,一個應用程序分配的棧內存大小在2M,因此這樣申請會報stack overflow
	/*C語言的內存分配 1.棧區(stack) 自動分配,自動釋放 ,通常是局部變量,用完立馬就釋放了. 2.堆區(heap) 程序員手動分配和釋放,能夠分配大概操做系統80%左右的內存. 3.全局區或靜態區 4.字符常量區 5.程序代碼區 */


	//在堆內存上分配一個400M的內存 
	// malloc 返回的是void* 表明,它能夠返回任意類型的指針 , 因此這裏
	int* p = malloc(1024 * 1024 * 100 * sizeof(int));

	
	getchar();
	//釋放該內存
	free(p);
}
複製代碼
  • 動態內存分配 +擴充
void main() {
	//靜態內存分配,數組的大小是固定的
	int len;
	printf("第一次輸入數組的長度:");
	scanf("%d", &len);

	
	int* p = malloc(len * sizeof(int));
	//int* p = calloc(len, sizeof(int)); //還有一種寫法 calloc 不用本身算,傳進去它自動幫你算
	//賦值
	int i = 0;
	//p 是內存地址第一個int 4字節的地址, 因此i++ 就直接給內存地址賦值就能夠了
	for (; i < len; i++)
	{
		p[i] = rand() % 100;
		printf("%d, %#x \n",p[i],&p[i]);
	}

	int addLen;
	printf("第二次增長數組的長度:");
	scanf("%d", &addLen);
	//realloc 從新分配內存, 以前開闢的內存指針, 須要擴容或縮小的大小,擴大會
	//擴大內存: 
	//1. 若果當前內存段後面有須要的內存空間,直接擴展,返回原指針
	//2. 當前內存段後面的空閒字節不夠, 那就使用堆中第一個能知足這一要求的內存區域,把原內存數據copy,而後釋放原內存,返回新的指針.
	//3. 若是申請失敗,返回NULL,原來的指針依然存在


	int* p2 = realloc(p,sizeof(int) * (len +addLen));
	if (p2 == NULL) {
		printf("從新分配內存失敗!!!!!!");
	}

	i = 0;
	for (; i < (len + addLen); i++)
	{
		p2[i] = rand() % 100;
		printf("%d, %#x \n", p2[i], &p2[i]);
	}

	if (p2 != NULL) {
		free(p2);
		p2 = NULL;
	}
	getchar();
}
複製代碼
  • 動態內存的釋放 和內存泄漏 不能屢次釋放,釋放完成後置NULL,標誌釋放完成 內存泄漏 指針從新賦值不等於 釋放前內存, 若是不作釋放 會發生內存泄漏.
void main() {
	//40M
	int *p1 = malloc(1024* 1024 * 10 * sizeof(int));
	//if (p1 != NULL) {
	// free(p1);
	// p1 = NULL;
	//}


	p1 = malloc(1024 * 1024 * 20 * sizeof(int));

	if (p1 != NULL) {
		free(p1);
		p1 = NULL;
	}


	getchar();
}

複製代碼
C 中的字符串

C語言中沒有字符串,字符串能夠用兩種方式表示一種是 字符數組,一種是字符指針 字符數組: 這裏的定長字符數組是固定的,能夠修改某個index 字符的值,可是一經建立,長度沒法改變. 字符指針: 字符指針是不能被修改的. 在C中也有像Java中同樣的一個處理String 的類,這個 #include <string.h>,它裏面有一些字符串的處理函數.
在線文檔:string在線文檔git

//C中字符串兩種建立方式
void main() {
	//1. 經過字符數組來 表示一個字符串
	//char str[] = {'c','h','i','n', 'a','\0'};
	//char str[6] = { 'c','h','i','n', 'a'};
	//char str[10] = "china";
	////能夠修改
	//str[0] = 's';
	//printf("%s \n", str);


	//getchar();



	//2. 經過字符指針 
	// 利用字符數組表示的能夠修改, 利用指針表示的數組不能被修改. 和java 中相似,string不可變,stringBuffer可變
	//內存連續排列
	char* str = "how are you?";

	//str[0] = 's'; //寫入位置時發生訪問衝突


	printf("%#x \n", str);
	printf("%s \n", str);
	str += 3;
	while (*str) {//取不到就爲0, 爲0就跳出了
		printf("%c", *str);
		str++;
	}
	getchar();
}
複製代碼
字符串的處理函數

在線API文檔 string在線文檔程序員

  • 字符串中匹配字符
void main() {
	char *str = "I want go to USA!";
	printf("%#x\n", str);

	char* p = strchr(str, 'w');
	if (p)
	{
		printf("索引位置:%d\n", p - str);
	}
	else
	{
		printf("沒有找到");
	}
	system("pause");
}
複製代碼
  • 字符串的拼接
void main() {
	char dest[50];
	char *a = "china";
	char *b = " is powerful!";
	
	strcpy(dest, a);
	strcat(dest, b);
	printf("%s\n", dest);
	system("pause");
}
複製代碼
結構體

結構體至關於Java 中的Object,可是它又有C中的部分獨有特性,不少時候咱們看jni.h相關的代碼或者WebRTC,會看到Google程序員會利用C結構體來模擬實現Java OOP編程的風格,使代碼更加清晰,易讀,易維護. 把不一樣的數據類型整合起來成爲一個自定義的數據類型github

  • 定義結構體
struct Man {
	//成員
	char name1[20];
	char* name;
	int age;
	int(*func)();
	void*(*funj)();
};

int test() {
	return 1;
}

void main() {
	//初始化結構體的變量
	//1. 第一種形式
	//struct Man m1 = {"Java",20,};
	//2. 聲明完成後,須要什麼本身賦值
	struct Man m1;
	m1.age = 23;
	m1.name = "java";

	m1.func = test;
	strcpy(m1.name1,"Android"); //name1 是char數組,因此不能直接賦值,只能使用strcpy的方式
	
	printf("%s,%s, %d, %d\n", m1.name,m1.name1, m1.age, m1.func());

	getchar();
}
複製代碼
  • 結構體變量 和 使用
struct Man {
	char *name;
	int age;
}m1, m2 = {"Java",20};

void main() {
	m1.name = "Android";
	m1.age = 10;
	getchar();
}

//匿名結構體,主要用在控制結構體變量的個數,至關於Java中單例
struct {
	int	age;
}m3;
void main() {
	m3.age = 5;
}
複製代碼
  • 基本寫法
// 結構體的嵌套: 這種寫法 和下面的寫法 沒有任何區別
//struct Student {
//	char name[20];
//	int age;
//	struct Tracher {
//		char name[20];
//	}t;
//};


struct Teacher {
	char name[20];
};

struct Student {
	char name[20];
	int age;
	struct Teacher t;
};
void main() {
	//struct Student s1 = { "java",20,{"json"} };

	struct Student s1;
	s1.age = 10;
	strcpy(s1.t.name,"json");
	system("pause");
}
複製代碼
  • 結構體於指針
struct Man {
	char name[20];
	int age;
};
void main() {
	struct Man m1 = {"Java", 20};
	struct Man *p = &m1;
	printf("%s,%d \n",m1.name,m1.age);
	printf("%s,%d \n",(*p).name,(*p).age);
	//-> 是(*p).的簡寫
	printf("%s,%d \n", p->name,p->age);
	system("pause");
}
複製代碼
  • 結構體數組
struct Man {
	char name[20];
	int age;
};

int main() {
	struct Man mans[] = { {"Java", 20},{ "Android", 25}};
	//遍歷結構體數組
	//1.
	struct Man *p = mans;
	for (; p < mans+2; p++)
	{
		printf("%s, %d\n",p->name, p->age);

	}
	//2.
	int i = 0;
	for (; i < 2; i++)
	{
		printf("%s, %d\n", mans[i].name, mans[i].age);
	}
	//3.
	int i = 0;
	for (; i < sizeof(mans) / sizeof(struct Man); i++)
	{
		printf("%s, %d\n", mans[i].name, mans[i].age);
	}

	//結構體的大小(字節對齊)

}
複製代碼
  • 結構體的大小 和字節對齊的概念 結構體的大小該怎麼計算呢?,那得先說說什麼是字節對齊:
    結構體必須知足 最大的數據類型的sizeof的值整除, 因此呢全部的屬性呢都會變成8個字節,即便空出來一塊.
    結構體變量的大小必須是最寬基本數據類型的整數倍. 若是不夠,那麼他就會在這些成員中間須要添加填充字節.
struct Man {
	int age;
	double weight;
};
void main() {
	struct Man m1 = {20, 170.5};
	printf(" %#x,%d,\n", &m1, sizeof(m1));
	getchar();
}
複製代碼
  • 結構體與動態內存分配
struct Man {
	char  *name;
	int age;
};

void main() {
	struct Man *m_p = (struct Man*)malloc(sizeof(struct Man) *10);
	struct Man *p = m_p;
	賦值
	p->name = "Jack";
	p->age = 20;
	p++;
	p->name = "Android";
	p->age = 21;

	struct Man *loop_p = m_p;
	for (; loop_p < m_p +2; loop_p++)
	{
		printf("%s, %d\n",loop_p->name ,loop_p->age);
	}
	free(m_p);
	getchar();
}
複製代碼
  • 結構體取別名
    typedef 取別名:
  1. 不一樣的名稱表明在幹不一樣的事情,好比:typedef int jint;
  2. 不一樣狀況下,使用不一樣的別名
#if defined (_cplusplus);
//typedef _JNIEnv JNIEnv;
//typedef _JavaVM JavaVM;
*/
//struct Man {
//	char name[20];
//	int age;
//};
//
//typedef struct Man JavaMan;
//typedef struct Man* JM;
//
//typedef int Age; //Age int類型的別名
//typedef int* Ap;//Age int 類型指針的別名;

//結構體取別名
typedef struct Woman {
	char name[20];
	int age;
} W,*WP;//W是Woman 結構體的別名, WP 是woman 結構體指針的別名

void main() {
	int i = 5;
	Ap a = &i;

	W w1 = {"Java",20};
	WP wp1 = &w1;
	printf("%s ,%d \n", w1.name, w1.age);
	printf("%s ,%d \n",wp1->name, wp1->age);

	getchar();
}
複製代碼
  • 結構體函數指針成員 C語言到最後搞來搞去,寫的都是這樣的代碼:
typedef struct Girl {
	char* name;
	int age;
	void(*sayHi)(char*);
}Girl;
typedef Girl* GirlP; //Girl結構體指針取別名GirlP
//Girl 結構體相似於Java中的類.name和age 相似於屬性,sayHi相似於方法.
void sayHi(char* text) {
	MessageBox(0,text,"title",0);
}

void rename(GirlP gp) {
	gp->name = "Android";
	//若是傳Girl 類型進來,會從新開闢一個內存空間,用完就銷燬了, 因此須要傳指針進來
}
void main() {
	struct Girl g1 = {"Lucy",18,sayHi};
	g1.sayHi("hello");

	GirlP gp = &g1;
	rename(gp);
	gp->sayHi("Byebye!");
	getchar();
}
複製代碼
聯合體 (共用體)

不一樣類型的變量共同佔用一段內存(相互覆蓋),聯合變量任何狀況只有一個內存存在,目的(節省內存). 聯合體變量大小= 最大的成員所佔的字節數算法

union MyValue {
	int x;
	int y;
	double z;
};
void main() {
	union MyValue d1;
	d1.x = 90;
	d1.y = 100;
	d1.z = 23.8; //最後一次賦值有效
	printf("%d,%d,%lf \n",d1.x, d1.y,d1.z);
	getchar();
}
複製代碼
枚舉

枚舉 :固定的數據 enumeration(主要用來列舉全部的狀況,限制值,保證取值的安全性)編程

enum Day {
	Mon,
	Tue,
	Wed,
	Thu,
	Fri,
	Sat,
	Sun
};
void main(void) {
	//枚舉的值必須是括號中出現的值
	enum Day d = Mon;

	printf("%#x,%d\n",&d,d);
	getchar();

}
複製代碼
C語言中的文件操做
  • 讀取和寫入文本
//寫入
void main() {
	char path[] = "D:\\BaiduNetdiskDownload\\test.txt";
	//打開
	FILE *fp = fopen(path, "w");
	char *text = "測試;\n啦啦啦啦啦";
	fputs(text, fp);
	//關閉流
	fclose(fp);
	getchar();
}
//讀取文本文件
void main()
{
	char path[] = "D:\\BaiduNetdiskDownload\\test.txt";
	//打開
	FILE *fp = fopen(path,"r");
	if (fp == NULL) {
		printf("文件打開失敗...");
		return;
	}
	//讀取
	char buff[50];
	while (fgets(buff,50,fp)) {
		printf("%s", buff);
	}
	//關閉
	fclose(fp);
	getchar();
}
複製代碼
  • 文件複製 二進制讀取寫入文件,計算機的文件存儲在物理上都是二進制,文本和二進制之分,實際上是一種邏輯之分,c讀寫文本文件與二進制文件的差異僅僅體如今回車換行,寫文本時,每遇到'\n',會將其轉成'\r\n'(回車換行),讀文本時,每遇到一個'\r\n',都會將其轉爲'\n'.
void main() {
	char path[] = "D:\\BaiduNetdiskDownload\\timg.jpg";
	//讀的文件 b字符表示操二進制文件binary
	FILE *read_fp = fopen(path, "rb");
	char write_path[] = "D:\\BaiduNetdiskDownload\\timg1.jpg";
	FILE *write_fp = fopen(write_path,"wb");
	
	//複製
	int buff[50];
	int len = 0;
	//fread(buff, sizeof(int), 50, fp) buff,緩衝區的類型的大小,一次讀多少,read_fp
	while ((len = fread(buff,sizeof(int),50, read_fp)) != 0)
	{	
		fwrite(buff,sizeof(int),len,write_fp);

	}
	//關閉流
	fclose(read_fp);
	getchar();
}
複製代碼
  • 獲取文件大小
void main() {
	char path[] = "D:\\BaiduNetdiskDownload\\timg.jpg";
	FILE *fp = fopen(path, "r");
	//該函數能夠直接操做文件指針, 操做指針到文件的任何位置,能夠用來作斷點續傳和下載.
	//0表明偏移量, SEEK_END 定位的位置根據偏移量
	fseek(fp, 0 ,SEEK_END); 
	//返回當前文件指針,至關於文件開頭的偏移量.
	long filesize = ftell(fp);
	printf("%ld\n", filesize);

	getchar();
}
複製代碼
  • 文件的加密和解密
//解密
void decrypt(char crypt_path[], char decrypt_path[], char password[]) {
	//打開文件
	FILE *crypt_fp = fopen(crypt_path, "rb");
	FILE *decrypt_fp = fopen(decrypt_path, "wb");

	//一次讀取一個字符
	int ch;
	int i = 0;
	int pwd_len = strlen(password);
	while ((ch = fgetc(crypt_fp)) != EOF) {
		fputc(ch ^ password[i % pwd_len], decrypt_fp);
		if (i == pwd_len)
		{
			i = 0;
		}
		else {
			i++;
		}
	}

	fclose(crypt_fp);
	fclose(decrypt_fp);
}
//加密函數
//異或 : 1^1 = 0 ; 0^0 = 0; 1^0 = 0
void encrypt(char normal_path[], char crypt_path[], char password[]) {
	//打開文件
	FILE *normal_fp = fopen(normal_path, "rb");
	FILE *crypt_fp = fopen(crypt_path, "wb");

	//一次讀取一個字符
	int ch;
	int i = 0;
	int pwd_len = strlen(password);
	while ((ch = fgetc(normal_fp)) != EOF) {
		fputc(ch ^ password[i % pwd_len], crypt_fp);
		if (i == pwd_len)
		{
			i = 0;
		}
		else {
			i++;
		}
	}

	fclose(normal_fp);
	fclose(crypt_fp);
}
void main() {
	char normal_path[] = "D:\\BaiduNetdiskDownload\\timg.jpg";
	char crypt_path[] = "D:\\BaiduNetdiskDownload\\timg_crypt.jpg";
	char decrypt_path[] = "D:\\BaiduNetdiskDownload\\timg_decrypt.jpg";
	//加密
	//encrypt(normal_path, crypt_path, "ceshimima");
	//解密
	decrypt(crypt_path, decrypt_path, "ceshimima");

	getchar();
}
複製代碼
C語言的預編譯 define

這裏得說一下爲何要用Visual Studio, VS 能夠看到內存狀況,能夠看到編譯日誌,.c 編譯完成後就是.obj的目標代碼.json

  • 預編譯: 爲編譯作準備工做,完成代碼文本的替換工做。
  • 編譯: 造成目標代碼(.obj)---()
  • 鏈接:將目標代碼和C函數庫鏈接合併,造成最終的可執行文件
  • 執行
/* 例如 這兩個頭文件互相依賴,須要執行某些事情 A.h #include "B.h" void printfA(); B.h #include "A.h" void printfB(); 在早期的編譯器必須這樣才能夠解決這種 頭文件的互相引用衝突. #ifndef BH #define BH #include "A.h" void printfB(); #endif // !BH 上面這個這個早期的處理只須要了解一下,新的編譯器這樣處理就能夠了 #pragma once */

//2. 定義常數
#define MAX 100

//3. 定義一個宏函數
void wy_com_jni_read() {
	printf("read\n");
}
void wy_com_jni_write() {
	printf("write\n");
}
#define jni(NAME) wy_com_jni_##NAME();

//4. 日誌輸出
//__VA_ARGS__ 表明可變參數. FOEMAT前加 ##表明參數,不加也能運行.
#define LOG(LEVEL,FORMAT,...)printf(##LEVEL);printf(##FORMAT,__VA_ARGS__);
#define LOG_I(FORMAT,...) LOG("INFO:",##FORMAT,__VA_ARGS__);
#define LOG_E(FOEMAT,...) printf("ERRO:");printf(##FOEMAT,__VA_ARGS__);
#define LOG_W(FORMAT,...) LOG("WARN:",##FORMAT,__VA_ARGS__);
void main() {
	//#include "my.txt" //給my.text 裏面就寫一個函數myprintf(char *msg){printf("%s",msg);};
	//myprintf("java"); //預編譯同樣能夠經過,就是個文本替換.函數同樣能夠獲得執行.

	//printfA();
	
	//2. 測試常數
	int i = 90;
	if (i < MAX) {
		printf("比MAX小");
	}
	
	//3.調用宏函數,只須要傳入name就能夠了 自動拼接成須要的函數名而且執行.
	jni(read); //調用的時候就看起來比較清爽了.
	
	//4. 測試 自定義LOG
	LOG_E("%s,%d\n","lallala",50); //測試本身的

	getchar();
}
複製代碼

結束語: C語言真的是博大精深,上面的這些例子只是C語言的一角,而咱們學習只是爲了當時的需求,而個人需求就是可以基本看懂jni.h, 可以使用JNI開發需求App就夠了,然而學到最後你會發現,會的越多忘的越快. 因此,若是你當下須要學習C語言,能夠把上面的代碼敲一遍,雖然筆記是CV大法+一目十行,可是,敲一遍運行一下,提高和找錯真的很快.

C語言學習資料

網站資料大全
C語言相關的書籍
  • C程序設計語言 《The C Programming Language》
  • C語言參考手冊 《C Reference Manual》
  • C primer plus第6版
  • C陷阱與缺陷 《C Traps and Pitfalls》
  • 指針的藝術
  • 你必須知道的495個c語言問題
  • C專家編程 《Expert C Programming》
  • 《C標準庫》
  • 《 C語言接口與實現 》
  • 《C語言的科學和藝術》
  • 《C程序設計語言》
  • 《數據結構與算法分析》
最終彩蛋
  • C++在C語言的基礎上嫁接了面向對象編程工具,面向對象是一門哲學,它經過對語言建模來適應問題,而不是對問題建模以適應語言。C++幾乎是C的超集,這意味着任何C程序差很少就是一個C++程序。學習C語言,也至關於學習了許多C++的知識。
相關文章
相關標籤/搜索