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++的知识。
相关文章
相关标签/搜索