什么是C语言
C语言一门计算机语言,主要应用于底层开发。
计算机语言的发展历程:
二进制语言(正电1/负电0)->汇编语言->B语言->C语言->C++、java等语言
第一个C程序
- hello world
#include<stdio.h> //standard input output
int main // 主函数,是程序的入口,有且仅有一个
{
printf("hello world") // printf函数----向控制台打印一条语句
return 0;
}
数据类型
char //字符数据类型
short //短整型
int //整型
long //长整型
long long //更长的整型
float //单精度浮点数
double // 双精度浮点数
#include<stdio.h>
int main()
{
char aaa='A';
printf("%c\n",aaa); // %c ----打印字符格式的数据
return 0;
}
#include<stdio.h>
int main(){
int age=20;
printf("%d\n",age); // %d ----打印整型十进制数据
return 0;
}
#include<stdio.h>
int main()
{
double a=5.34;
printf("%lf\n",a); // %lf ----打印双精度浮点数
return 0;
}
//----%d----打印整型
//----%c----打印字符
//----%f----打印浮点型--小数
//----%p----打印地址
//----%x----打印十六进制
- 每种类型的大小是多少?
#include<stdio.h>
int main()
{
printf("%d\n",sizeof(char)); //1字节 取值范围是0~(2^8)-1(减一是因为0占一位)
printf("%d\n",sizeof(int)); //4字节 取值范围是0~[2^(8*4)]-1
printf("%d\n",sizeof(short)); //2字节 取值范围是0~[2^(8*2)]-1
printf("%d\n",sizeof(long)); //4或8字节 ·
printf("%d\n",sizeof(long long)); //8字节 ·
printf("%d\n",sizeof(float)); //4字节 ·
printf("%d\n",sizeof(double)); //8字节
}
*计算机中的单位*
计算机是通电的硬件(正电 1 负电 0)
十进制: 0 1 2 3 4 5 6 7 8 9
十二进制(时钟):0 1 2 3 4 5 6 7 8 9 10 11 12
七进制(星期):0 1 2 3 4 5 6
二进制:0 1
bit----------比特位(即电信号0和1)
byte---------字节(规定8个bit=1个byte)
kb-----------(1个kb=1024个byte)
mb-----------(1个mb=1024个kb)
gb ·
tb ·
pb ·
- 为什么出现这么些个类型?
比如说,要设置一个变量来存放人的年龄,这时就要用到整型,整型中有 short、int、long、long long,如果用int、long以及long long,显然范围太大造成存储空间的浪费。 - 二进制向十进制转化
10000101=(1*2^7)+(1*2^2)+(1*2^0)=133
- 十进制向二进制转化
131=128+2+1=(2^7)+(2^1)+(2^0)
变量、常量
常量
生活中不变的量,如圆周率π,自然常数e,身份证号
- 常量分类
字面常量 (例如:3)
const修饰的常变量
#include<stdio.h>
int main (void)
{
const int aa=4; //const修饰常变量
printf("%d\n",aa);
// aa =8; //这里不能对aa变量 重新赋值,变量有了常属性就不能再重新赋值了
printf("%d\n",aa);
return 0;
}
#include<stdio.h>
int main(void)
{
const int n=10; //n是变量,但是又有常属性,所以我们说n是常变量
int nn[n]={0};
//在定义数组的时候,括号内的元素个数必须是常量,这里因为n还是一个变量(虽然有了常变量的特性),所以程序不通过
return 0;
}
#define定义的标识符常量
#include<stdio.h>
#define max 10
int main(void)
{
int nn[max]={0}; //这里定义的标识符常量是可以使用的,与上面的额const定义的常变量性质不同
printf("%d\n",max);
return 0;
}
枚举常量 (一一列举出来的常量)
#include<stdio.h>
enum sex //枚举常量
{
man,
woman,
select
};
int main(void){
enum sex people=men; //将枚举常量men赋值给变量people
printf("%d\n",man);
printf("%d\n",woman);
printf("%d\n",select);
return 0;
}
变量
#include<stdio.h>
int main()
{ short age =20; //向内存申请2个字节=16bit,用来存放20
float weight =95.6f; //编译器默认95.5双精度类型,这时将其定义为单精度类型,存在精度丢失,所以以95.6f声明定义为单精度类型
return 0;
}
- 变量的定义方式
int aaa=50;
float a=50.6f
char ad=‘ss’
- 变量的分类
全局变量 (在{}外定义的变量)
局部变量 (在{}内定义的变量)
#include<stdio.h>
int a=100; //全局变量
int main(void){
int ab=10; //局部变量
printf("%d\n",ab);
return 0;
}
- 变量的使用
举个例子:
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int main()
{
int num1=0;
int num2=0;
int sum =0;
scanf("%d%d",&num1,&num2); //&取地址,向存放num1和num2的地址输入数值 scanf---输入函数
sum=num1+num2;
printf("%d",sum);
return 0;
}
就像在中国960万平方公里的土地上,因为有了一个人的地址信息,我们就可以准确找到内蒙古科技大学23号楼xxx室,
内存也像是这样,如4g/8g内存分成了一块一块的地址块,使用&符号就可以准确找到变量在计算机中存放的地址
- 变量的作用域和生命周期
作用域
#include<stdio.h>
int aa = 10; //全局变量的作用域是整个工程
int main()
{
int num = 100;
printf("num=%d\n",num); //局部变量num的作用域就是main函数的{}
printf("aa=%d\n", aa);
return 0;
}
生命周期
局部变量生命周期:进入作用域生命周期开始,出作用域生命周期结束
全局变量生命周期:整个程序的生命周期
注意:局部变量和全局变量可以以同名同时存在,建议不要重名,容易误会
当局部变量和全局变量名字相同的时候,局部变量优先
C语言语法规定,变量要定义在当前代码块的最前面
字符串+转义字符+注释
- 字符串
字符串是由双引号包住的
字符串"asd"
空字符串""
printf("%s\n",)
#include<stdio.h>
int main(void){
char aa[]="abc"; //aa数组中的值为 a b c /0
char bb[]={'a','b','c'}; //bb数组中的值为 a b c
printf("%s\n",aa);//abc末尾默认放一个/0(字符串的结束标志),所以打印abc
printf("%s\n",bb);//将单个字符以字符串的形式打印,没有遇到字符串的结束标志,则打印随机值,打印abc烫烫烫烫蘟bc
printf("%d\n",strlen(aa)); //strlen计算字符串长度 ,abc,/0不算
printf("%d\n",strlen(bb)); //由于没有字符串结束标志/0,所以这个字符串的长度将无法确定,是一个随机值
return 0;
}
ASCII码,规定每个字符以十进制的形式存储起来(对计算机就转换成对应的二进制)
例如:
/0----0
65-----A
66-----B
97-----a
- 转义字符
例如:
\n 换行
\t 水平制表符
\v 垂直制表符
\" 双引号
\\ 反斜杠(防止\被转义)
\ddd 八进制数字 例如 \60
\xdd 十六进制数字 例如 \x32
int main(void){
printf("%d\n",strlen("c:\test\32\test.c")); //13个字符(\t算一个转义字符,\32算一个转义字符)打印值为13(\t,\32,\t一共是三个字符)
printf("%c\n",'\32'); //先将8进制的32转换成十进制数字26,作为ASCII码值26代表->,对应的字符将是输出的结果
printf("%c\n",'\x61'); //先将16进制的61转换成十进制数字97,作为ASCII码值97代表a,对应的字符将是输出的结果
return 0;
}
- 注释
注释方法一:
/* 可以包住一大段代码
*/
注释方法二:
\\ 注释一行代码
选择语句
#include<stdio.h>
int main(void)
{
int input =0;
printf("加入比特\n");
printf("你要好好学习吗?(1/0)>:");
scanf("%d",&input);
if(input==1)
printf("好offer\n");
else
printf("卖红薯\n");
return 0;
}
循环语句
#include<stdio.h>
int main(void)
{
int line=0;
printf("加入比特\n");
while(line<20000)
{
printf("敲一行代码");
printf("%d\n",line);
line++;
}
printf("好offer");
return 0;
}
函数
#include<stdio.h>
int main()
{
int sum=0;
int num1=100;
int num2=200;
int a=10;
int b=20;
sum=Add(num1,num2); //调用Add函数
printf("sum=%d\n",sum);
sum=add(a,b);
printf("sum2=%d",sum);
return 0;
}
int Add(int x,int y) //自己定义一个Add函数
{
int z=x+y;
return z;
}
数组
#include<stdio.h>
int main(void){
int aa[10]={1,2,3,4,5,6,7,8,9,10}; //定义一个放10个元素的整型数组 (除了可以定义int型的数组,还可以定义char,float等)
int i=0;
while(i<10)
{
printf("%d\n",aa[i]); //打印下标为i的元素,此aa数组一共10个元素,对应的下标为0 1 2 3 4 5 6 7 8 9,下标是为了方便访问单个元素
i++;
}
return 0;
}
操作符
- 算术操作符
+ - * / %
- 移位操作符(二进制数字)
<< 左移 >> 右移
Int a=1;
整型1占4个字节----32个比特位
00000000000000000000000000000001
a<<1
00000000000000000000000000000010
Int b=a<<1;
B的输出结果为2
- 位操作符
&按位与 (规则:对应的二进制位1与0为0,1与1为1)(即:全真为真,一假为假)
(十进制)3----011(二进制)
(十进制)5-----101(二进制)
3&5=001-------十进制1
|按位或 (规则:对应的二进制位1或0为1,0或0为0)(即:一真为真,全假为假)
^按位异或
//按位异或规则:对应的二进制位相同,则为0,对应的二进制位相异,则为1
#include<stdio.h>
int main(void)
{
int a=5; //101
int b=3; //011
int c=a|b; //111=7
printf("%d\n",c);
int d=a^b;
printf("%d\n",d); //按位异或规则:对应的二进制位相同,则为0,对应的二进制位相异,则为1 a^b=110=6
return 0;
}
- 赋值操作符
= += -= *= /= %= ^= |= >>= <<=
#include<stdio.h>
int main(void){
int a=10;
printf("%d\n",a);
printf("%d\n",!a); //输出0,即为假
return 0;
}
- 单目操作符
! 逻辑反操作
- 负值
+ 正值
& 取地址
sizeof类 型长度(以字节为单位)
~ 对一个数的二进制取反
-- 前置、后置--
++ 前置、后置++
* 间接访问操作符(解引用操作符)
(类型) 强制类型转换
#include<stdio.h>
int main(void)
{
int a=10;
printf("%d\n",a);
printf("%d\n",sizeof(a)); //sizeof是计算空间的大小,单位是字节
printf("%d\n",sizeof(int));
int aa[10] = { 0 };
int s = 0;
printf("%d\n", sizeof(aa));
s = sizeof(aa) / sizeof (aa[0]); //计算数组内元素的个数 数组大小/单位元素大小
printf("元素个数=%d\n", s);
return 0;
}
#include<stdio.h>
int main (void)
{
int a =10;
int aa[]={1,2,3,4,5,6};
printf("%d\n",sizeof(a)); //int型 4字节
printf("%d\n",sizeof(int));
printf("%d\n",sizeof a); //sizeof属于一个操作符,如果是一个函数,是需要括号的
// printf("%d\n",sizeof int); error
printf("%d\n",sizeof(aa));
printf("%d\n",sizeof(aa)/sizeof(aa[0])); //aa数组大小/aa数组第一个元素的大小==aa数组的元素个数
return 0;
}
#include<stdio.h>
int main(void)
{
int a=0; //int 占4个字节,==32比特位 二进制表示00000000000000000000000000000000
int b=~a; //~是按位取反 二进制表示11111111111111111111111111111111
printf("%d\n",b);
//源码 反码 补码
//负数在内存中存储的时候,存储的是二进制的补码
//第一位为符号位 0为正数 1为负数
//打印的时候,打印的是源码
//打印b 首先要将b存储起来,打印的时候再来打印
//11111111111111111111111111111111存储的结果是11111111111111111111111111111110存储时,先将b的符号位不变,然后减1 ,符号位不动,其余位取反
//得到10000000000000000000000000000001
}
//只要是整数,内存中存储的都是二进制的补码
//正数的源码 反码 补码 相同
//负数
//原码 --------- ------》反码--------》补码
//写出二进制序列 其他位按位取反 反码+1
#include<stdio.h>
int main(void){
int a=10;
// int b=a++; //后置++,先使用,再++ 结果a=11 b=10
int b=++a; //前置++,先++,后使用 结果a=11 b=11
printf("a=%d b=%d",a,b);
return 0;
}
#include<stdio.h>
int main(void)
{
int a=(int)3.14; //强制类型转换 ,此处3.14是浮点型,编译会有警告,使用强制类型转换,会处理掉这个错误,最终输出结果为3
printf("%d",a);
return 0;
}
- 关系操作符
> >= <= < != ==
- 逻辑操作符
&& 逻辑与
|| 逻辑或
#include<stdio.h>
int main(void)
{
int a=3; //真------非0 即为真
int b=0; //假------0
int c=a&&b; //逻辑与
int d=a||b; //逻辑或
printf("%d\n",c);
printf("%d\n",d);
return 0;
}
- 条件操作符(三目操作符)
#include<stdio.h>
int main(void)
{
int a=10;
int b=250;
int max=0;
max=(a>b?a:b); //条件操作符又称三目操作符
printf("%d\n",max);
return 0;
}
等价于下面
#include<stdio.h>
int main (void)
{
int a=10;
int b=20;
int max=0;
if (a>b)
max=a;
else
max=b;
printf("%d\n",max);
return 0;
}
- 逗号表达式
exp1,exp2,exp3...
- 下标引用、函数调用和结构成员
#include<stdio.h>
int main(void){
int aa[10]={2};
printf("%d\n",aa[5]); //[]下标引用操作符
int a=10;
int b=20;
int sum=add(a,b); //()函数调用操作符
printf("sum=%d\n",sum);
return 0;
}
int add(int x,int y){
int z=0;
z=x+y;
return z;
}
常见关键字
关键字不能与变量名冲突
auto auto int a=10 定义局部变量使用,不过现在我们一般省略掉了
break case char const continue default do double
else enum float for goto if int long return short
static struct switch typedef union void volatile while
signed int a=10;实则是signed int a=10; //定义一个有符号的数字
unsigned int num=-1; //定义一个无符号数,即使放一个负数,num也当做是正数
extern 引入外部符号,在全局变量跨源文件使用时候会用到
register 寄存器管理关键字,计算机存储数据,硬盘(500G)->内存(8G/4G)->高速缓存->寄存器
CPU从内存中拿到数据,然后计算,随着科技的发展,CPU处理数据越来越快,所以CPU逐渐去高速缓存去拿数据,这样处理起来就更快
register int a=10; //建议把a定义到寄存器里,最终由计算机根据实际情况判定
- 关键字 tppedef
类型名重命名
#include<stdio.h>
int main ()
{
unsigned int num1=10;
typedef unsigned int u_int;
u_int num2=20; //这里的num2与num1都是无符号int型
return 0;
}
- 关键字 static
#include<stdio.h>
int main(void)
{
int i=0;
while (i<5)
{
testa();
i++;
}
return 0;
}
void testa(){
static int a=1; //a是一个静态的局部变量 static修饰局部变量,局部变量的生命周期变长
a++;
printf("a= %d\n",a);
}
//extern 申明外部符号,在一个.c文件创建全局变量,在另一个.c文件中申明其为外部符号,即可在两个文件中使用这个全局变量
//extern同样可以申明外部函数,效果与申明全局变量一样
//static修饰全局变量,将会改变全局变量的作用域,使得全局变量只能在一个.c文件中使用,这时外部声明不再起作用
//static修饰函数,改变了函数的链接属性(外部链接属性->内部链接属性)效果与static修饰全局变量相同
#define定义常量和宏
- 定义标识常量
- 定义宏
#include<stdio.h>
#define MAX(X,Y) (X>Y?X:Y) // 定义宏MAX
int Max(int x,int y) //定义函数Max
{
if (x>y)
return x;
else
return y;
}
int main(void)
{
int a=10;
int b=20;
//函数解决
int max=Max(a,b);
printf("较大值为:%d\n",max);
//宏解决
int max1=MAX(a,b);
printf("max:%d",max1);
return 0;
}
指针
先了解内存
内存:
一个大的酒店,因为有规定好的房间号,所以,酒店服务人员可以快速而准确的找到每一个
房间,并为之提供必要的服务。
内存也像是一个酒店一样,整个内存被分成一块一块的内存地址块,将每一个 地址块进行编
号,计算机就能快速的找到地址,我们在创建变量的时候就是这样:向计算机申请一块地址空
间(该地址空间有特定的地址编号),来存放变量,当我们向想输出/使用该变量的时候,计算
机就可以根据我们的指令,找到相应的地址空间,取出存放在该地址空间的变量,为之使用。
32位操作系统和64位操作系统
32位:有32根物理电线,将整个内存排序的时候,将会产生2^32个空间位置(每根电线有正负
信号之分,即0和1),而内存空间将会被分为单独的地址块,每个地址块如果只存放1bit,
则一块内存也就只能存储2^32bit(0.5gb),这样是不合理的。所以,一个内存块存储1byte,
这样一块内存将会存储0.5*8=4gb,显然这样安排是合理的。
64位:同上
#include<stdio.h>
int main(void)
{
int a=10;
int* p=&a; //有一种存放地址的变量-----指针变量
printf("%p\n",&a);
printf("%p\n",p);
*p=50; //解引用操作符 ,对a地址存放的值进行更改
printf("改变以后的a的值为:%d\n",a);
return 0;
}
int main(void)
{
char a = 'a';
char *ba = &a;
printf("原始a的值:%c\n", a);
char b = *ba = 's';
printf("解引用改变地址中存储的内容,结果为:%c\n", b);
printf("%d", sizeof(ba));
return 0;
}
- 指针变量的大小:
32位:由于32位系统的一个内存空间存放的是32个比特位,也就是4byte
64位:由于64位系统的一个内存空间存放的是64个比特位,也就是8byte
结论:指针在32位操作系统是4byte大小,指针在64位操作系统是8byte大小,(不论指针类型是什么)。
结构体
如果要表示一个复杂对象,如 人、书...
这时候就需要创建出一个复杂对象---结构体
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<string.h>
struct book //创建一个结构体类型
{
char name[20]; //书名
short price; //价格
int ISBN; //ISBN号
}; //注意:此处有分号
int main()
{
struct book b1 = { "C语言程序设计", 55 };
printf("书名:%s\n", b1.name);
printf("价格:%d元\n", b1.price);
b1.price = 15; //变量可以直接赋值修改
strcpy(b1.name, "C++"); //数组需要使用strcpy库函数进行修改
printf("修改后的书名:%s\n", b1.name);
printf("修改后的价格是:%d元\n", b1.price);
struct book*p = &b1;
printf("%s\n", (*p).name);
printf("%d\n\n\n", (*p).price);
printf("%s\n", p->name);
printf("%d\n", p->price);
return 0;
}
//. 结构体变量.成员
//-> 结构体指针->成员
整体感悟
现在跟着鹏哥学习C语言,和以前是完全不一样的,之前我们的上课形式是在教室里,老师在讲台上对着PPT读课件,我们在下面跟着听,不仅没有电脑实际操作,还得让我们下来好好看书,顺便照着电脑敲代码,这样下来,我们的学习效果和学习的积极性都被磨灭了。尤其是越学到后来,什么指针、结构体、取地址这些的,我们都已经学的云里雾里了,根本不懂。
现在看来,这样的学习刚好是很有成效的:一边跟着视频学习,一边记笔记,同时还有一点,学习C语言这几天,我发现,还是要先对C语言要有一个 整体的认知,然后再细学,就像现在这样,先初识C语言,然后在逐步细化。
其实指针也不难,就是一种变量,能存放内存地址的特殊变量。