C语言基础
这是很简单的C语言,如果想深入了解,可以自己自学,C语言毕竟可以说是高级编程语言的基础呀,书籍一堆一堆的。
我们要学习C语言,使用C语言开发要怎么搞呢?
首先就是一个必要的开发工具,这里推荐开源工具Dev-C++,一款用于编译C语言的工具,就像eclipse开发java一样,除了安装Dev-C++之外,不在需要其他工具了。
C之Hello world
打开Dev-C++工具,直接选择新建或者快捷键CTRL+N新建一个C文件,然后我们输入以下代码:
#include
#include
main(){
printf("helloworld!\n"); //System.out.println(); "\n"换行符
system("javac Hello.java");
system("java Hello");
system("notepad");
system("pause"); //system执行windows的系统命令
}
然后保存到命名为C 的目录中,将文件保持为hello.c,但这只是一个原始代码,继续在Dev-C++中的菜单栏找到运行,然后选择编译,这时你会发现在hello.c的旁边出现一个hello.exe。
哈,是不是很神奇,我们自己搞出一个exe可执行文件!
当然我们现在还不用执行hello.exe,我们要在C目录下添加一个Hello.java(标准的java文件)的文件夹,里面输入Hello,java就行了。
这时执行hello.exe,就会打开命令提示符,也就是CMD命令框。
首先会输入“helloworld!”,然后编译,接着执行Hello.class,最后停止。
这便是运行流程。那么我们来说说文件中代码的含义吧!
其中
#include // 相当于 java的import .h c的头文件 stdio.h standard io 标准输入输出
#include // stdlib standard library 标准函数库 java.lang
在写C语言的时候添加上这两句话准没错的。
然后main(){ }这是一个主程序入口,就类似于java中的public static void main(String[] args)。
最后就是system(),这相当于system执行windows的系统命令 ,所以在执行system("javac Hello.java");是相当于我们在命令提示符中直接输入javac Hello.java。
C语言的基本数据类型
前面我们已经学会了C语言的Hello world,现在我们要了解一下C语言的数据类型了,我们直接通过与java对比,看看他们直接有多大区别
java基本数据类型 所占字节 C数据类型 所占字节
boolean 1
byte 1
char 2 char 1个字节
short 2 short 2
int 4 int 4
long 8 long 4
float 4 float 4
double 8 double 8
上面是java和C直接都有的数据类型,同时对比双方所占字节数,但是上面并不是C所有的数据类型,C一共有以下几种数据类型:
char, int, float, double, long, short, signed, unsigned, void
* signed 有符号数 最高位是符号位 可以表示负数 但是表示的最大值相对要小
* unsigned 无符号数 最高位是数值位 不可以表示负数 表示的最大值相对要大
* signed unsigned 只能用来修饰整形变量 char short int long
* C没有 boolean byte C用0和非0表示false true
上面我们了解C所占的字节数,但是如何验证呢?可以通过以下代码验证:
#include
#include
// char, int, float, double, long, short, signed, unsigned, void
// signed unsigned 有符号 无符号 只能用来修饰整形变量 char int short long 默认有符号
// sizeof(int)
main(){
printf("char占%d个字节\n", sizeof(char));
printf("int占%d个字节\n", sizeof(int));
printf("short占%d个字节\n", sizeof(short));
printf("float占%d个字节\n", sizeof(float));
printf("long占%d个字节\n", sizeof(long));
printf("double占%d个字节\n", sizeof(double));
unsigned char c = 128;
printf("c = %d\n",c);
system("pause");
}
其中%d表示的是int类型的占位符,请大家注意,在使用占位符的时候千万不要写错!然后sizeof(int)这个方法表示的是其中的变量所占的字节数。
占位符的使用
刚才说到%d这个占位符,那么其他的占位符又是怎样的呢?
%d - int
%ld – long int
%lld - long long
%hd – 短整型
%c - char
%f - float
%lf – double
%u – 无符号数
%x – 十六进制输出 int 或者long int 或者short int
%o - 八进制输出
%s – 字符串
以上就是C语言中的少数占位符,使用代码如下:
#include
#include
main(){
char c='a';
short s = 123;
int i = 12345678;
long l = 1234567890;
float f = 3.1415;
double d = 3.1415926;
printf("c = %c\n", c);
printf("s = %hd\n", s);
printf("i = %d\n",i);
printf("l = %ld\n",l);
printf("f = %.4f\n",f); //默认输出的是6位有效数字的小数 想手动指定 加上.X
printf("d = %.7lf\n",d);
printf("%#x\n",i);
printf("% #o\n",i);
//char cArray[]={'a','b','c','d','\0'}; // C数组[]要写在变量名后面, \0表示的是字符串结束符,这种方式不能输入汉字
//char cArray[]="abcdefg"; // 这种方式不用手动写入结束符
char cArray[]="你好"; // 输入汉字
printf("cArray = %s",cArray);
system("pause");
}
C语言之输入函数
学习计算机语言逃不过一个宿命,那就是讨论内存地址,而C语言中更是讲究。
在这里我们先通过键盘输入班级人数,然后通过取出count的地址来保存班级人数
#include
#include
//scanf("占位符",内存地址)
main(){
printf("请输入班级的人数:");
int count;
scanf("%d", &count); //&取地址符
printf("班级的人数是%d\n",count);
char cArray[20];//c的数组不检测下标越界
printf("请输入班级的名字:");
scanf("%s",&cArray);
printf("班级的人数是%d,班级的名字%s\n",count,cArray);
printf("count的地址%d\n",&count);
printf("cArray的地址%d\n",&cArray);
system("pause");
}
C中的指针
C语言中的指针可以说是一个重点难点,比较难以理解,因为我们内存地址让人难以理解,所以这里只是抛砖引玉的粗略的讲讲指针的概念。
关于符号*
其中int* pointer 表示声明一个int类型的指针变量pointer
x * y 表示乘法运算
*pointer; 取出指针变量pointer 中保存的内存地址对应的内存中的值
接着我们来看看指针的运用:
#include
#include
main(){
int i = 123;
//一般计算机中用16进制数来表示一个内存地址
printf("%#x\n",&i);
//int* int类型的指针变量 pointer指针 指针变量只能用来保存内存地址
//用取地址符&i 把变量i的地址取出来 用指针变量pointer 保存了起来
//此时我们可以说 指针pointer指向了 i的地址
int* pointer = &i;
//int *pointer ; int * pointer
printf("pointer的值 = %#x\n",pointer);
printf("*pointer的值%d\n",*pointer);
*pointer = 456;
printf("i的值是%d\n",i);
system("pause");
}
运行这段代码,会得到以下结果:
0x62fe44
pointer的值 = 0x62fe44
*pointer的值123
i的值是456
其中i的值为123,但是计算机给i分配一块内存地址既是0x62fe44这个空间,然后这个空间存放的值为123,这便是i=123的含义。
然后我们在创建了int类型的指针变量point,当然指针变量point也获得了自己的一个内存地址。
同时通过&i将i的内存地址取出,赋予了point,也就是说指针变量point的内存地址的值存入了0x62fe44这个内存地址,而通过 0x62fe44内存地址是可以找到123这个值的。
那么如何在指针变量point中取出123呢?使用pointer便可以取出。
最后pointer = 456就是将0x62fe44这个内存地址的值重新赋值为456,而i的内存地址是指向0x62fe44的,所以当0x62fe44的值发生改变时,i的值也随之发生改变。
这里面有点绕,不好理解,但指针方面的知识确实要好好学习一下。
指针的错误使用
在使用指针之前要注意对其进行赋值,没有赋值就进行操作的指针被称为野指针,在Windows中是不允许这样操作的,因此指针使用之前要初始化 赋给它一个自己程序中声明的变量的地址
其次,指针使用的时候要注意 int类型的指针要指向int类型的内存地址, double类型的指针要指向double类型的地址,如果乱指会出bug
下面是错误的示例代码:
#include
#include
/**
*/
main(){
//注意,这是错误的示例代码
int i;
double d = 3.1415;
int* pointer = &d;
printf("pointer的值=%#x\n",pointer);
printf("*pointer = %d\n",*pointer);
system("pause");
}
C语言交换两数的值
一个传统的问题,如何交换两数的值,以下代码演示
#include
#include
/**
*值传递 和引用传递 值传递和引用传递传递的实际上 都是数值 只不过引用传递传递的是地址值
*如果想通过一个子函数来修改main函数中变量的值 一定要用引用传递
*/
// 值传递
swap(int i, int j){
int temp = i;
i = j;
j = temp;
}
// 引用传递
swap2(int* pointer, int* pointer2 ){
int temp = *pointer;
*pointer = *pointer2;
*pointer2 = temp;
}
main(){
int i = 123;
int j = 456;
// swap(i,j); // 无法交换两数的值
swap2(&i,&j); // 通过交换内存地址中的值,成功交换两数的值
printf("i的值%d,j的值%d\n",i,j);
system("pause");
}
为什么swap方法无法交换i和j的值呢?举个不恰当的例子,那边是局部变量和成员变量的区别,首先swap外部有i和j,然后传入swap之后,i和j便与外部的i和j断绝了关系,因此在内部交换之后无法将交换信息传递到外部。
而swap2采用的是引用传递,通过获取内存地址,改变内存地址之中的值,这时才能真正的改变两数的值
c中数组与指针的关系
数组实际上就是一块连续的内存空间,而数组变量名的地址实际上是第一个元素的地址,因此我们通过指针获取数组变量名的内存地址,可以说是获得了数组的内存地址。
同时我们通过对内存地址的加减法就能获得数组中的元素。需要注意的是对内存地址的加减法中,相加得到的结果实质上与数据类型有关,如我们使用指针变量*(pointer+0),如果是int类型的数组,实质上会加上4个字节,得到下一个数组元素,char类型的数组也是一样
#include
#include
main(){
// char array[] = {'a','b','c','d','\0'};
int array[] = {1,2,3,4};
printf("array[0]的地址%#x\n",&array[0]);
printf("array[1]的地址%#x\n",&array[1]);
printf("array[2]的地址%#x\n",&array[2]);
printf("array[3]的地址%#x\n",&array[3]);
printf("array的地址%#x\n",&array);
//数组变量名的地址实际上是第一个元素的地址
char* pointer = &array;
//int* pointer = &array;
char array2[] = "hello from c"
char* pointer2="hello from c";
//printf("%s\n",pointer2);
/*
// char类型中每次运算是相加1个字节
printf("*(pointer+0)=%c\n",*(pointer+0));
printf("*(pointer+0)=%c\n",*(pointer+1));
printf("*(pointer+0)=%c\n",*(pointer+2));
printf("*(pointer+0)=%c\n",*(pointer+3));
*/
// 在int类型数组中,指针变量每次运算结果是相加4个字节
printf("*(pointer+0)=%d\n",*(pointer+0));
printf("*(pointer+1)=%d\n",*(pointer+1));
printf("*(pointer+2)=%d\n",*(pointer+2));
printf("*(pointer+3)=%d\n",*(pointer+3));
system("pause");
}
C语言中指针变量的字节数
指针变量的所占字节数与所指向的数据类型无关,与操作系统有关,32位操作系统地址总线是32位,用4个字节的变量来保存32位操作系统的内存地址 1byte 8位 4*8=32
因此32位操作系统 指针变量占4个字节,而64位操作系统 指针变量占8个字节 。
#include
#include
/**
32位操作系统地址总线是32位 4个字节的变量来保存32位操作系统的内存地址 1byte 8位 4*8=32
32位操作系统 指针变量占4个字节
64位操作系统 指针变量占8个字节
*/
main(){
int* pointer;
double* pointerD;
printf("int类型的指针变量占%d个字节\n",sizeof(pointer));
printf("double类型的指针变量占%d个字节\n",sizeof(pointerD));
system("pause");
}
C语言之多级指针
多级指针:数星星,有几个星就是几级指针。其中二级指针只能保存一级指针的内存地址,同意三级指针只能保存二级指针的内存地址。
那么如果想通过三级指针拿到最初的变量的值,就使用 ***point3
#include
#include
main(){
int i = 123;
//int类型的一级指针
int* pointer = &i;
//int类型的二级指针 二级指针只能保存一级指针的地址
int** pointer2 = &pointer;
//int类型的三级指针
int*** pointer3 = &pointer2;
// 取出初始值
printf("***pointer3 = %d\n",***pointer3);
system("pause");
}
C主函数获取临时变量的地址
main函数获取子函数中临时变量的地址,此时要注意值传递和引用传递的区别,我们想要获得的是内存地址,因此必须传入内存地址,同时二级指针才能保存一级指针的内存地址,因此子函数中要传入的变量应为二级指针
#include
#include
function(int** pointer){
int i = 123;
*pointer = &i;
printf("i的地址%#x\n",&i);
}
main(){
int* pointer1;
function(&pointer1);
printf("pointer1的值%#x\n",pointer1);
system("pause");
}
C语言中的内存地址回收
栈内存由系统统一分配统一回收
静态内存分配 栈内存大小固定的 内存地址是连续的
#include
#include
int* getData(){
int array[] ={1,2,3,4,5};
printf("%#x\n",&array);
return &array;
}
int* getData2(){
int array[] ={5,4,3,2,1};
printf("%#x\n",&array);
return &array;
}
main(){
int* pointer = getData();
getData2();
printf("%d,%d,%d\n",*(pointer+0),*(pointer+1),*(pointer+2));
printf("%d,%d,%d\n",*(pointer+0),*(pointer+1),*(pointer+2));
printf("%#x\n",pointer);
system("pause");
}
在这一例子中,pointer指向的是getData()中的内存地址,可是为什么得到的结果却是getData2()的值呢?那是因为在执行完getData()之后,获得的栈内存地址被回收,然后直接被getData2()使用,导致其中的值被修改。所以使用指针的时候要注意,栈内存只能使用一次,之后会出现同一块内存地址被其他程序占用的情况。如果想让内存始终保持一个只,那么则要使用堆内存
c中的动态内存分配
java new对象就会申请一块堆内存,而C语言则要手动申请,使用malloc申请一个固定的内存大小,如malloc(sizeof(int)*5)申请5个int类型大小的内存,注意申请了堆内存需要手动释放,如果忘记释放容易造成内存泄漏。
其中对内存的特点:不连续的;大小不固定,取决机器的状态
#include
#include
//c malloc memory allocation 内存分配
main(){
//malloc 接收的参数 申请内存大小 返回一个内存地址值 申请到的也是一块连续的内存空间
int* pointer = malloc(sizeof(int)*5);
*(pointer+0) = 1;
*(pointer+1) = 2;
*(pointer+2) = 3;
*(pointer+3) = 4;
*(pointer+4) = 5;
//C for 循环 循环的临时变量i 要先声明再使用
int i;
for(i = 0;i<5;i++){
printf("第%d个元素的值= %d\n",i,*(pointer+i));
}
free(pointer);
printf("第一个元素的值%d\n",*(pointer+0));
system("pause");
}
学生学号管理系统
#include
#include
/**
保存班级人数
申请一块堆内存保存学生的学号
来了几个插班生
扩展一下堆内存
保存插班生的学号
realloc re-
*/
main(){
printf("请输入班级的人数:");
int count;
scanf("%d",&count);
//申请一块堆内存
int* pointer = malloc(sizeof(int)*count);
int i;
for(i = 0;i
C的结构体
c结构体类似java的class,使用struct来声明c的结构体。
结构体的大小大于等于结构体中每一变量的占字节数的和,
结构体的大小是最大的那个变量所占字节数的整数倍。如果想获得结构体中的变量,可通过获取结构体指针,然后通过结构体指针获得结构体变量
注意C结构体中不能定义函数,如果想要在结构体中调用方法,可以使用函数指针。
函数指针的定义的方式:返回值(函数指针变量名字)(返回值),如下面示例代码中的void(studypointer)();
其中 -> 表示间接引用运算符,可以访问结构体里面的变量,如示例代码中的stuPointer->age
结构体的特点
- 结构体中的属性长度会被自动补齐,这是为了方便指针位移运算
- 结构体中不能定义函数,可以定义函数指针
- 程序运行时,函数也是保存在内存中的,也有一个地址
- 结构体中只能定义变量
- 函数指针其实也是变量,它是指针变量
- 函数指针的赋值: 函数指针只能指向跟它返回值和接收的参数相同的函数
#include
#include
void study(){
printf("good good study!\n");
}
struct Student{
int age; //8
int score; // 4
char sex; //1
void(*studypointer)();
} ;
main(){
struct Student stu = {18,100,'f'};
stu.studypointer = &study;
stu.studypointer();
struct Student* stuPointer = &stu;
printf("*stuPointer.age = %d\n",(*stuPointer).age);
(*stuPointer).sex ='m';
printf("stu.sex = %c\n",stu.sex);
printf("stuPointer->age = %d",stuPointer->age);
//printf("stu.age = %hd\n",stu.age);
//printf("stu.score = %d\n",stu.score);
//printf("stu.sex = %c\n",stu.sex);
// printf("结构体student占%d个字节\n",sizeof(stu));
system("pause");
}
C中的联合体
联合体占字节数取决于,其中成员占内存空间最大的那一个 。
联合体只能保存一个变量的值,并且联合体共用同一块内存。
#include
#include
/**
*/
union u{
int num; //4个字节
double d; //8个字节
}
main(){
union u u1;
u1.num = 123;
u1.d = 123.456;
printf("union占%d个字节\n",sizeof(u1));
printf("u1.num = %d\n",u1.num);
system("pause");
}
C中的枚举
C中的枚举使用enum,同时枚举中的元素可赋值
#include
#include
/**
*/
enum weekday{
MON=9, TUE, WEND,THUR,FRI,SAT,SUN
};
main(){
enum weekday day = MON;
printf("day= %d",day);
system("pause");
}
C中的自定义类型
自定义类型使用typedef可给类型赋予别名,如 typedef int i;此时能将i当作int类型使用。
用途:如果结构体名字过长,可起到省略的作用,其中在Android的jni中大量使用了typedef设置别名,便于辨认数据类型
// 简单实例:
#include
#include
/**
*/
typedef int i;
main(){
i j = 123;
printf("j = %d\n",j);
system("pause");
}
如在之前的结构体中有
struct Student{
···
} ;
可更改为:
typedef struct Student{
int age; //8个字节
int score; // 4个字节
char sex; //1个字节
// 使用函数指针调用函数
void(*studypointer)();
} stud;
此时在main函数中就可以直接使用stud代替Student,如下:
main(){
stud stu = {18,100,'f'};
// 函数指针指向函数的内存地址
stu.studypointer = &study;
····
}