经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » C 语言 » 查看文章
初识C语言 - L-wk
来源:cnblogs  作者:L-wk  时间:2021/12/15 9:05:59  对本文有异议

初识C语言

大家好,我是新来的菜鸟,我来开始给大家讲解自己理解的C语言。讲的不好还望不要喷的太厉害了(手动滑稽)。谢谢大家的观看!!!


什么是C语言?

首先我们讲一下啥是语言,人和人不就是用语言交流嘛,那语言种类有很多种比如说汉语呀、英语(想必大家多多少少都学过一点吧,我这种菜鸟就是英语没学(手动滑稽))、德语或者日语等等其他语言。那我们知道了语言就是用来进行交流的嘛。人和人可以用上面所述的语言交流,但是人和机器呢?那不就是机器语言咯(明知故问)。是的,机器语言,说到机器语言这就得提起我所熟悉的那几个,汇编语言(好像现在很少用了吧?)、C语言、C++、C#(有木有跟我刚见到这个语言一样的念成c井,其实它叫C sharp)、Java、Python呀,其他的我就好像听得比较少了吧,毕竟新人嘛,嘿嘿,都体谅体谅一下哈,多谢!回归正题什么是C语言,书上是这样说的:

C语言是一门通用计算机编程语言,广泛应用于底层开发。C语言的设计目标是提供一种能以简易的方式编译、处理低级存储器、产生少量的机器码以及不需要任何运行环境支持便能运行的编程语言。

尽管C语言提供了许多低级处理的功能,但仍然保持着良好跨平台的特性,以一个标准规格写出的C语言程序可在许多电脑平台上进行编译,甚至包含一些嵌入式处理器(单片机或称MCU)以及超级电脑等作业平台。

二十世纪八十年代,为了避免各开发厂商用的C语言语法产生差异,由美国国家标准局为C语言制定了一套完整的美国国家标准语法,称为ANSI C,作为C语言最初的标准。 [1] 目前2011年12月8日,国际标准化组织(ISO)和国际电工委员会(IEC)发布的C11标准是C语言的第三个官方标准,也是C语言的最新标准,该标准更好的支持了汉字函数名和汉字标识符,一定程度上实现了汉字编程。

C语言是一门面向过程的计算机编程语言,与C++,Java等面向对象的编程语言有所不同。

其编译器主要有Clang、GCC、WIN-TC、SUBLIME、**MSVC、Turbo C等。

看了这么多笼统的解释还是有繁琐的,所以就这样认为吧,C语言就是一门计算机编程语言,是人和计算机交流的语言。个人理解有错误的话希望大家指点指点,感谢!

第一个C语言程序

说了这么多概念,还是来点实际的吧,大家可以去网上搜索C语言编程软件,各种各样的都有,这里分享一下我使用的软件,我使用的是Vs2022,下面我给大家演示一下第一个C语言程序:

#include <stdio.h> //头文件的包含  //-->注释符号 
int main()
{
    printf("Hello World!\n");
    return 0;
}

上面是代码块,我们可以看到这个黑色的框框里面出现了HelloWorld这几个字符。

C语言规定每个程序都要主函数(main),C语言代码都是从主函数开始执行的。而且主函数有且只有一个哦。stdio.h 是一个头文件 (标准输入输出头文件) , #include 是一个预处理命令,用来引入头文件。 当编译器遇到 printf() 函数时,如果没有找到 stdio.h 头文件,会发生编译错误。return 0; 语句用于表示退出程序。printf()函数是打印函数,就是用来在屏幕上打印信息的。

数据类型

数据类型什么是数据类型呢?在生活中有许多不一样的数据,比如说我有一个朋友他年龄20岁,身高190cm,名字叫“小顾”,或者比如说买个瓶水2元钱,或者是买菜16.9元钱.....等等。我们需要记录这些不同类型的数字,在计算机中就需要不同的数据类型来表示。而C语言中基本数据类型有这几种:

char //字符数据类型
short //短整型
int //整形
long //长整型
long long //更长的整形
float //单精度浮点数
double //双精度浮点数

解释一下这几个不同类型吧,char类型是用来表示一个字符类型的,就比如说字符a。而short int long long long这几个都是用来表示整数的,比如说20、100、50000等等。float double这两种呢就是用来表示小数的,比如说3.1415....后面列举不出来了忘了这个圆周率了,只是说它们的精度不同也就是小数点后面的个数不同。那C语言没有字符串类型吗?也就是像这样的“i like China”。好像确实没有明确哪种类型是字符串类型,但是有个是用char [12]="i like China"表示字符串类型的。

那既然有这么多类型,那它们的大小是多少呢?C语言中规定求类型大小是用sizeof关键字,大小是字节

#include <stdio.h>
int main()
{
    printf("%d\n", sizeof(char));
    printf("%d\n", sizeof(short));
    printf("%d\n", sizeof(int));
    printf("%d\n", sizeof(long));
    printf("%d\n", sizeof(long long));
    printf("%d\n", sizeof(float));
    printf("%d\n", sizeof(double));
    printf("%d\n", sizeof(long double));
    return 0;
}

通过观察我们可以知道

数据类型 类型大小
char 1
short 2
int 4
long 4
long long 8
float 4
double 8

大家第一次看到这个字节可能有点疑问,其实换个符号大家可能就认识了,byte,大家看见有木有点熟悉,不熟悉的话再换一个KB或者MB、GB这样大家应该会熟悉了。其实在计算机当中,单位有比特位,字节这一说,计算机中最小的单位是字节,而一个字节是8个比特位。一个比特位很简单,就是0或者1。

说明一下:为什么会出现这么多不同类型呢?

这是因为每个类型所占的内存空间大小不同,所以我们合理使用不同的类型,可以节省内存空间,避免浪费嘛,你说是吧(手动滑稽)。

变量和常量

看见这两个词会怎么理解?我用最通俗易懂的话来讲:变量就是那些可以被改变的值。常量就是不能被改变的值。

举个例子:

变量:年龄、体重、薪资、存款等.....

常量:圆周率、身份证号码、血型等.....

变量

定义变量

在C语言中定义变量的方法是怎样的呢?

之前了解了数据类型是什么,定义变量那就是想要什么类型的变量就定义什么类型的变量。

C语言定义变量的方式是:类型 变量名 = 初始化值;

int age  = 20;
float weight = 45.5f;
char ch = 'w';

变量的分类

  • 局部变量 定义在函数体内或者说 在{}内部
  • 全局变量 定义在函数体外
#include <stdio.h>
int a = 10;//全局变量
int main()
{
    int a = 100;//局部变量
    return 0;
}

通过上面代码大家应该知道了什么是全局变量和局部变量了吧。

看为什么出错了呢?虽然a变量是在函数体中,也是局部变量,但是为什么就错了呢?那是因为{}限制它的作用范围。再看

这样又没有问题了,所以说在函数内部的虽然是局部变量但是它的作用范围会被限制的,作用范围下面会说到的,各位客官请勿着急。

接下来我们看个例子

#include <stdio.h>
int b = 2019;//全局变量
int main()
{
    int a = 2020;//局部变量
    //这里局部变量与全局变量同名了 会发生什么呢?
    int b = 2021;//局部变量
    printf("b = %d\n",b);
    return 0;
}

可以看到,当全局变量与局部变量同名时候,优先使用的是局部变量

变量的使用

我们了解了怎么定义变量,现在我们了解怎么去使用它。

计算两个数的和,num1,num2,从键盘输入数值,求和然后打印到屏幕上代码如下:

#include <stdio.h>
int main()
{
    int num1 = 0;
    int num2 = 0;
    int sum = 0;
    printf("输入两个操作数:>");
    scanf("%d %d", &num1, &num2);
    sum = num1 + num2;
    printf("sum = %d\n", sum);
    //这里介绍一下输入,输出语句
    //scanf 输入语句 顾名思义是向计算机输入值
    //printf 输出语句 上面有使用过,就是在屏幕上打印一段信息
    return 0;
}

大家如果也是用的是VS2022或者VS2019,在这个代码运行时会出现一个错误,那就是scanf函数unsafe,意思就是这个函数不安全,这个时候我们可以在后面加上 _s 就变成了scanf_s,或者大家不想这么麻烦的话就在源文件最上面加上define _CRT_SECURE_NO_WARNINGS 1即可。

上述图片只是展示了变量的一种用法,后续还会有其他用法,之后用到再说。现在只是初步认识一下C语言。

变量的作用域和生命周期

之前有提到过作用范围。而接来下要讲的作用域就跟作用范围有关。

作用域

作用域(scope),程序设计概念,通常来说,一段程序代码中所用到的名字并不总是有效/可用的
而限定这个名字的可用性的代码范围就是这个名字的作用域 。

可以这样理解作用域就是作用范围,个人的理解哈。我们用代码形式来观察

可以看见,程序可以正常运行,那我们看下面:

这下报错了,为啥呢?这就是作用域的问题了。看完局部变量,我们来看全局变量:


可以看见全局变量作用域是整个工程。得出结论

  1. 局部变量的作用域是变量所在的局部范围。
  2. 全局变量的作用域是整个工程。

生命周期

看见生命周期我们会想到什么?人的一生?从出生到离开这个世界,这也算是是生命周期。

官方的解释是:

变量的生命周期指的是变量的创建到变量的销毁之间的一个时间段。

那变量的声明周期呢?变量什么时候创建、什么时候销毁,这之间的时间段。

用事实来说话,眼见为实,咱们看代码效果图:

我们之前讲作用域时,不输入下面那条printf()函数语句时是不会报错的,但是添加了后为什么又报错了呢?这就是这个变量a的生命周期到了,出了{}后a变量生命周期到了就自行销毁了。所以后面的输出语句执行就会报错。全局变量的生命周期应该不用我说了吧(手动滑稽)?跟全局变量的作用域有关。

小结:

  • 局部变量的生命周期:进入作用域生命周期开始,出作用域声明周期结束。
  • 全局变量的生命周期:整个程序的生命周期。程序结束了全局变量生命周期也就到头了。

常量

常量:不可变的值。C语言中定义常量和定义变量的形式是不同的。

C语言中常量可以分为以下几种

  • 字面常量

  • const修饰的常变量

  • #define定义的标识符常量

  • 枚举常量

#include <stdio.h>
#define MAX 1000 //#define的标识符常量
//枚举常量
enum COLOR
{
    RED,
    GREEN,
    BLUE
}
//RED,GREEN,BLUE皆是枚举常量
int main()
{
    //1.字面常量
    3.1415926;//浮点型常量
    1000;//整型常量
    'a';//字符常量
    "hello"//字符串常量
    //2.const修饰常变量
    //int num = 20;//变量
    const num = 20;
    num = 50;//加上const后能否改变?
    printf("num = %d\n",num);
    return 0;
}

看代码作用还是小了点,咱们看点实际点的。

可以看到num这个变量是可以被改变的,接下来再看:

加上const就修改不了了,这是怎么回事?因为被const修饰的变量叫做常变量,具有常属性,常属性就是不能被改变的属性,虽然不能被改变,但是它本质上还是个变量。

我们创建数组时,[]内要是个常量,大家可以看到错误信息,这说明n是个变量。你这不是废话嘛又没加上const修饰。那好我们加上const修饰一下看看会怎样?

喏,看见没,即便加上const修饰,结果还是一样的,这证明了什么,这就证明了const修饰的变量,本质上还是个变量,只是具备了常属性,也就是不能被修改的属性。

再看一个枚举常量,枚举就是一一列出来,像三原色、性别等等可以一一列举出来的值。它们都是有实际值得。我们也可以打印出来的。

这里我把#define定义的标识符常量也放进去了,定义了枚举常量三原色,并且也一一列举了,可以看到#define定义的也是个常量,无法被修改,再看枚举常量它们其实也是有值的,而且还是递增的值,但能不能改呢?其实不能,但是可以给它赋初值。

可以看到赋初值后,下一个也会跟着上面的初值改动而改动的。

字符串+转义字符+注释

字符串

什么是字符串?在C语言中,字符串就是一串字符。怎么表述的呢?用“”括起来的一串字符就是字符串。

最重要的一点是,字符串结束的标志是一个‘\0’的转移字符。因为在计算字符串长度的时候‘\0’是结束标志,不算做字符串的内容,但是用数组存储字符串的时候,数组的长度需要将‘\0’计算上,只是单独计算字符串长度是不计算‘\0’

#include <stdio.h>
int main()
{
    //C语言中用字符数组存储字符串,数组也就是一组类型相同的元素的集合
    char str1[]="hello";
    char str2[]={'h','e','l','l','o'};
    char str3[]={'h','e','l','l','o','\0'};
    printf("%s\n",str1);
    printf("%s\n",str2);
    printf("%s\n",str3);
    return 0;
}

我们来看一下上面代码的执行效果:

咦?为什么第二个跟其他两个不一样呀?这就是跟字符串结束的标志有关。我们调试一下看看这几个数组中放的是什么。

可以看到第一个和第三个数组放的都是一样的,而第二个少了个‘\0’,而且第一个数组不是5个字符吗,怎么多了一个,之前说字符串结束的标志是‘\0’,因为第一个数组是用来存储字符串的,所以会默认在结尾加上‘\0’只是不会显示出来。但是在计算数组长度的时候就会把‘\0’加上去。接着说第二个为啥是这样的,因为第二个数组最后一个不是‘\0’所以会在内存中后面去找‘\0’,找到‘\0’之前的数据也会被打印出来,所以第二个就是会是这样打印的。

转义字符

转义字符?啥是转义字符呢?字面意思,转变意思的字符。

如果我们要在屏幕上打印一个目录:d:\vs2022\test.c。我们该如何写代码呢?之前我们用过printf函数打印信息。

#include <stdio.h>
int main()
{
    printf("d:\vs2022\test.c\n");
    return 0;
}

为啥跟我们期待的不一样呢?我们看代码上面有语法高亮,\v \t \n这个都改变了颜色,说明它们是一样类型的。之前打印其他信息的时候会看见‘\n’,这个我们通过观察应该知道这是一个换行的标志,相当于在末尾加了一个回车键。这就是转义字符了,那在C语言中又有那些转移字符呢,下面我列一张表给大家观看观看。

转义字符 意义
? 在书写连续多个问号时使用,防止它们被解析成三字母词
\ ' 用于表示字符常量 ‘
\ ‘’ 用于表示一个字符串内部的双引号
\ \ 用于表示一个反斜杆,防止它被解释为一个转移字符
\a 警告字符,会发出蜂鸣声
\b 退格符号
\f 进制符号
\n 换行
\r 回车
\t 水平制表符号
\v 垂直制表符号
\ddd ddd表示1~3个八进制数字。如:\157X
\xdd dd表示2个十六进制数字。 如:\xFF 1
#include <stdio.h>
int main()
{
    printf("%c\n",'\'');
    printf("%s\n", "\"\" ");
    return 0;
}

会打印什么呢?

这样大家知道转移字符的用法了吗?下面我们再看一道题提示:strlen()是计算字符串长度(不计算‘\0’哦)。

#include <stdio.h>
int main()
{
    printf("%d\n", strlen("abcdef"));
	// \32被解析成一个转义字符 8进制里是不会有8的哦
	printf("%d\n", strlen("c:\test\328\test.c"));
    return 0;
}

大家答案是多少呢?6 18 还是6 14呢?

答案是6 18 的小伙伴算错了哦。大家可以看上面的转义字符表再来计算哦。

注释

注释大家应该一下子就能理解吧,就是用来解释代码的意思或者是有些不需要的代码也可以注释哦。

注释用两种风格:

  • C语言风格的注释 /*xxxxxxx*/

    • 缺陷是不能嵌套注释
  • C++风格的注释//xxxxxxxx

    • 可以注释一行也可以注释多行

选择语句

选择语句顾名思义就是用来选择的语句。就好比,我们进入了学校,是好好学习呢?还是不学习呢?好好学习,毕业了就有好工作,不学习,毕业了就回家种田吧。这就是选择。选择语句也叫分支语句。

graph TD A[进入学校] --> C{好好学习?} C --> |好好学习| D[好工作] C --> |不学习| E[回家种田]

用代码的形式表示:

#include <stdio.h>
int main()
{
    int option = 0;
    printf("你会好好学习嘛?(选择 1 or 0):")
    scanf("%d",&option);
    if(1 == option)
    {
        printf("好好学习,你会有好工作\n");
    }
    else
    {
        printf("不学习,回家种田吧\n");
    }
    return 0;
}

还有其他形式嘛?会有的,在后续的博客里都会有,现在只是初步认识一下C语言。

循环语句

顾名思义就是循环的做某一件事情。比如我们选择日复一日的学习。

C语言中如何实现循环呢?

  • while语句
  • for语句
  • do...while语句
#include <stdio.h>
int main()
{
    printf("加入学习\n");
    int line = 0;
    while(line <=30000)
    {
        line++;
        printf("敲代码\n");
    }
    if(line>30000)
    {
        printf("成为高手\n");
    }
    return 0;
}

这里是以while循环来举个例子,后面的两个循环会在后续文章讲到。看一下运行的效果如何:

?

看到箭头指向的滚动条,上面其实还有很多行信息,这就是循环语句的效果,重复的做某一件事情。

函数

大家看到函数会想到什么?我们学过数学中的函数。数学中的函数是怎么样的?f(x)=2*x+5。这就是数学中的函数。C语言中的函数呢?

函数是一段可以重复使用的代码,用来独立地完成某个功能,它可以接收用户传递的数据,也可以不接收。接收用户数据的函数在定义时要指明参数,不接收用户数据的不需要指明,根据这一点可以将函数分为有参函数和无参函数。

在C语言中函数是这样定义的:函数的返回类型 函数名(函数参数类型 函数参数名){函数体,函数要实现的功能}。我们把上面那段相加的代码改造一下:

#include <stdio.h>
int main()
{
    int num1 = 0;
    int num2 = 0;
    int sum = 0;
    printf("输入两个操作数:>");
    scanf("%d %d", &a, &b);
    sum = num1 + num2;
    printf("sum = %d\n", sum);
    return 0;
}
上述代码,写成函数如下:
#include <stdio.h>
int Add(int x, int y)
{
    int z = x+y;
	return z;
}
int main()
{
    int num1 = 0;
    int num2 = 0;
    int sum = 0;
    printf("输入两个操作数:>");
    scanf("%d %d", &num1, &num2);
    sum = Add(num1, num2);
    printf("sum = %d\n", sum);
    return 0;
}

我们看实现的效果:

这是第一段代码的实现效果,下面我们看用函数的形式是怎么样的:

我们发现效果是一样的,只是说相加的功能是在Add函数内部去实现了。因此我们知道函数的特点:

简化代码,提高代码的复用性。

这只是自定义函数,在C语言中还有许多库函数可以给我们使用,就是我们用到的printf 、scanf等。

数组

在介绍数组之前,问一下大家,我们要定义1~10的数字,是怎样定义呢?是一个一个的定义嘛?这样会不会太麻烦了,其实也不是不行,那换作是100个或者是1000个呢?那我们这样一个变量一个变量的定义实属麻烦,所以我们要引用——数组这个概念了。

数组是一组相同类型元素的集合。就好比我要存整数1~10。

数组定义

在C语言中是这样定义的:类型名 数组名[数组元素个数]={初始值}

int arr[10]={1,2,3,4,5,6,7,8,9,10}
char arr[5]={'a','b','c'};

可以使用如上代码定义一个任何类型的数组,当然除了那几种基本数据类型哈。定义后我们可以对它进行初始化,也可不进行初始化,或者只初始化一部分就比如我上面创建的字符数组。不初始化的话,数组元素的值会被定义成随机值的,所以一般在我们建立数组后,我们都将它初始化为0,和定义变量一样,这是一个好习惯哦。

数组的使用

数组都是通过下标来访问的。上代码:

#include <stdio.h>
int main()
{
    int i = 0;
    int arr[10] = {1,2,3,4,5,6,7,8,9,10};//定义数组并初始化
    for(i = 0;i < 10;i++)
    {
        printf("%d ",arr[i]);//打印数组内容
    }
    printf("\n");
    return 0;
}

以上代码意思是定义了一个有十个整型大小的数组并在定义时进行初始化,然后使用for循环将数组元素打印出来。for循环后面会讲。数组的使用:通过数组名+下标的方式进行引用,arr[i],其中[]里的数字就是我们的数组下标。大家可以看到数组下标是从0开始的,即想要获取数组第一个元素可以写成:arr[0],因此我们知道数组下标最大的是 数组元素个数-1,第一个元素下标为0,以此类推。

操作符

指令系统的每一条指令都有一个操作符,它表示该指令应进行什么性质的操作。操作符也就是不同的操作符有对应不同的操作嘛。

操作符的分类

  1. 算术操作符
  2. 移位操作符
  3. 位操作符
  4. 赋值操作符
  5. 单目操作符
  6. 关系操作符
  7. 逻辑操作符
  8. 条件操作符
  9. 逗号表达式
  10. 下标引用操作、函数调用和结构成员

<span id="“1"">算术操作符

操作符 作用
+
-
*
/
% 取余(取模)

1.加、减、乘和平常数学中运算规则一样。

2./(除)这个操作符需要注意一点:除的时候如果除号(/)两边只要有一个或者两个浮点数,采用的是浮点型除法,若两边都是整型,则采用整数的除法,没有余数。

3.%这个操作符两边只能是整数,它返回的是整除之后的余数。

算术操作符还算简单,需要注意的地方我也标出来了。

移位操作符

操作符 作用
<< 左移操作符,二进制序列左移
>> 右移操作符,二进制序列右移
  1. 移位操作符作用的对象是一个数的补码;
  2. 对于左移:左边抛弃,右边补0;
  3. 对于右移:右边抛弃,左边补原来的符号位(采用算术移位);
  4. 对于’>>’操作符,分为两种移位规则:一种逻辑移位,另一种是算术移位,
  • a.逻辑移位规则是右边抛弃,左边最高位直接补0,不考虑之前数的符号位。
  • b.算术移位规则是右边抛弃,左边最高位补和之前数相同的符号位。而目前编译器通常采用的是算术移位规则。
  1. 但这里需要注意,对一个数移位操作完成后,当前的数不会改变的,除非把它赋值给另外一个变量。
  2. 对于移位操作符,不要移动负数位,这个是标准未定义的。

上面提到了补码,我要强调一个概念 原码反码补码 (如果没有基础的,建议百度下)

并且一个整数4个字节,由32位组成

其中正数的 原码反码补码 相同

负数的 反码等于原来相对应的正整数的原码所有位按位取反.

负数的 补码等于其反码最低位 加 1

2>>1

00000000 00000000 00000000 00000010 //这是原码
00000000 00000000 00000000 00000001 //这是右移1之后

2<<1

00000000 00000000 00000000 00000010 
00000000 00000000 00000000 00000100 //这是左移之后

-1 << 1

10000000 00000000 00000000 00000001//这是-1的原码 最高位 是符号位
11111111 11111111 11111111 11111110//这是-1的反码
11111111 11111111 11111111 11111111//这是-1的补码
11111111 11111111 11111111 11111110//这是-1左移1之后 这是补码! 
11111111 11111111 11111111 11111101//补码-1
10000000 00000000 00000000 00000010//这是补码还原之后 值为-2

-1 >>1

11111111 11111111 11111111 11111111//这是-1的补码
11111111 11111111 11111111 11111111//这是-1右移1之后第一个1是加上去的 右边丢弃左边补补符号位。
int main()
{
    int a=0,b=0,n=2;
    a=n<<1;
    printf("%d",a);
    b=n>>1;
    printf("%d",b);
}

观察输出的值可以得出结论:左移操作相当于给之前的数乘2,右移操作相当于给之前的数除2;

位操作符

位操作符针对的是二进制位的。

操作符 作用
& 按位与
| 按位或
^ 按位异或
  1. 同样这里位操作符作用的对象也是一个数的补码,并且它们的操作数必须是整数。
  2. 对于’&’,两个数补码对应位置的值都为1,结果为1,否则为0;对于’|’,两个数补码对应位置都是0,结果是0,否则为1;

赋值操作符

赋值操作符很简单,顾名思义就是赋值用的。赋值操作符其实就是=。但是其他的也叫复合赋值符

操作符 作用
= 给一个变量赋值
+= 相加之后赋值
-= 相减之后赋值
*= 相乘之后赋值
/= 相除之后赋值
&= 按位与之后赋值
^= 按位异或之后赋值
|= 按位或之后赋值
>>= 右移之后赋值
<<= 左移之后赋值

用复合操作符可以使代码更简洁。

单目操作符

操作符 作用
! 逻辑取反操作
+ 正值
- 负值
& 取地址
sizeof 操作数的类型长度(以字节为单位)
~ 对一个数的二进制按位整体取反
-- 前置、后置--
++ 前置、后置++
* 间接访问操作符(解引用操作符)
(类型) 强制类型转换
  1. sizeof操作符计算的是变量(类型)所占空间的大小,是按字节来计算,重要的是,sizeof (a+b)里面的表达式不参与计算,若a,b都是int行类型,其结果依然是4;
  2. 当数组作为参数为函数传参时,由于数组要发生降级,其会降级为一个指针,如果此时在一个函数中sizeof计算数组的大小是不可以的,其计算的是数组降级为指针的大小(4个字节),所以,若函数要得到一个数组的大小,应该在主函数中计算。
  3. 对于前置++或 - -,先操作,再使用,对于后置++或 - -,先使用,再操作。

关系操作符

关系操作符就是用来判断两者之前的关系的。

操作符 作用
> 大于
>= 大于等于
< 小于
<= 小于等于
!= 不等于
== 等于

这些关系操作符,比较简单,不多说,但需要注意,不要将等于(==)写成赋值操作(=)

逻辑操作符

逻辑操作符用来判断两者之前逻辑关系的,与和或,想必大家高中有学过。

操作符 作用
&& 逻辑与
|| 逻辑或

不同与按位与和按位或。

条件操作符

操作符 作用
exp1?exp2:exp3 exp1为真,结果为exp2,反之exp3

注:exp是表达式的意思 比如说 ( x > y ) ? x : y;

逗号表达式

操作符 作用
exp1,epx2,....expN 从左往后依次执行,整个表达式结果是最后一个表达式的结果

比如:int a = 3,b = 4;c = ( a > b, a = b * 4, b = a + 2);结果是啥呢?我们来算算:

c = (3 > 4,a=4*4=16,b=16+2 ) 结果是18

下标引用操作、函数调用和结构成员

操作符 作用
[] 数组下标操作符,操作数:数组名+索引
() 函数调用操作符
. 访问结构体成员
- > 访问结构体成员

用例子来解释吧:

int arr[10]={1,2,3,4,5,6,7,8,9,10};

arr[2] = 3;

Add(a,b) 函数调用

.操作符

struct people
{
  char name[10];//姓名
  int age;//年龄
  char sex[5];//性别
};
#include <stdio.h>
int main()
{
 	struct people x = {"许诺",20,"男"};
    // .操作符访问结构体成员
    printf("姓名:%s\n",x.name);
    printf("年龄:%d\n",x.age);
    printf("性别:%s\n",x.sex);
    return 0;
}

- >操作符

struct people
{
  char name[10];//姓名
  int age;//年龄
  char sex[5];//性别
};
#include <stdio.h>
int main()
{
 	struct people x = {"许诺",20,"男"};
    struct people n = &x;
    // ->操作符访问结构体成员
    printf("姓名:%s\n",n->name);
    printf("年龄:%d\n",n->age);
    printf("性别:%s\n",n->sex);
    return 0;
}

. ---> 结构体对象.成员名 -> ---> 结构体指针->成员名

常见关键字

关键字(Keywords)是由C语言规定的具有特定意义的字符串,通常也称为保留字,例如 int、char、long、float、unsigned 等。我们定义的标识符不能与关键字相同,否则会出现错误。

你也可以将关键字理解为具有特殊含义的标识符,它们已经被系统使用,我们不能再使用了。

标准C语言规定了32个关键字。

关键字 说明
auto 声明自动变量
short 声明短整型变量或函数
int 声明整型变量或函数
long 声明长整型变量或函数
float 声明浮点型变量或函数
double 声明双精度浮点型变量或函数
char 声明字符型变量或函数
struct 声明结构体变量或函数
union 声明共用数据类型(联合体or共同体)
enum 声明枚举类型
typedef 用给数据类型取别名
const 声明只读变量
unsigned 声明无符号类型变量或函数
signed 声明有符号类型变量或函数
extern 声明变量是在其他文件正声明
register 声明寄存器变量
static 声明静态变量或函数
volatile 说明变量在程序执行中可悲隐含地改变
void 声明函数无返回值或无参数,声明无类型指针
if 条件语句
else 条件语句否定分支(与if连用)
switch 用于分支语句
case 分支语句分支
for 循环语句
do 循环语句的循环体
while 循环语句的循环条件
goto 无条件跳转语句
continue 结束当前循环,开始下一轮循环
break 跳出当前循环
default 分支语句中“其他”分支
sizeof 计算数据类型长度
return 子程序返回语句(可以带参数,也可以不带参数)循环条件

这里提一下几个关键字:

typedef

typedef 类型定义,就是给已知的类型重新起个名字。

typedef unsigned int u_int;//给unsigned int 类型重新起了个名字叫 u_int 实际上还是unsigned int
int main()
{
    unsigned int num1 = 10;
    u_int num2 = 10;
    //其实这两个变量类型是相同的
    return 0;
}

static

在C语言中:

static是用来修饰变量和函数的。

  1. 修饰局部变量-静态局部变量
  2. 修饰全局变量-静态全局变量
  3. 修饰函数-静态函数

修饰局部变量

#include <stdio.h>
//1
void test()
{
    int a = 0;
    a++;
    printf("%d ",a);
}
//1 1 1 1 1 
int main()
{
    int i = 0;
    while(i<5)
    {
        test();
        i++;
    }
    return 0;
}
//2
void test()
{
    static int a = 0;//static修饰局部变量
    a++;
    printf("%d ",a);
}
//1 2 3 4 5
int main()
{
    int i = 0;
    while(i<5)
    {
        test();
        i++;
    }
    return 0;
}

对比一下代码1和代码2的输出结果,我们可以得出结论

static修饰局部变量改变了变量的生命周期,让静态局部变量出了作用域还存在不会自动销毁,直到程序结束,生命周期才结束。

修饰全局变量

这里需要两个源文件来举个例子:

//1
//test1.c
int a = 2021;
//test.c
int main()
{
    extern int a;
    printf("%d\n",a);
    return 0;
}
//2
//test1.c
static int a = 2021;
//test.c
int main()
{
    extern int a;
    printf("%d\n",a);
    return 0;
}

代码1效果:

可以看到程序正常运行。那我们看代码2效果:

可以看到程序报错了,为啥呢?全局变量,在其他源文件内部可以被使用,是因为全局变量具有外部链接属性但是被static修饰之后,就变成了内部链接属性,其他源文件就不能链接到这个静态的全局变量了。

一个全局变量被static修饰,使得这个全局变量只能在本源文件中使用,不能在其他源文件中使用。

修饰函数

这也用两个源文件来举例子:

//代码1
//test1.c
int Add(int x, int y)
{
	return x+y;
}
//test.c
int main()
{
    printf("%d\n", Add(2, 3));
	return 0;
}
//代码2
//test1.c
static int Add(int x, int y)
{
	return x+y;
}
//test.c
int main()
{
    printf("%d\n", Add(2, 3));
    return 0;
} 

我们来看效果:

程序也是正常运行,那加上static修饰一下试试:

报错了,跟上面修饰全局变量有点相似。可以得出结论。

一个函数被static修饰,使得这个函数只能在本源文件内使用,不能在其他源文件内使用。

本质上: static是将函数的外部链接属性变成了内部链接属性! ( 和static修饰全局变量一样! )

#define 定义常量和宏

之前常量有提到过#define定义常量,大家应该也有点印象吧。 define其实是一个预处理指令。

define的用法:

define 定义符号 常量

#define MAX 1000

define 定义宏

#define ADD(X,Y) ((X)+(Y))

我来解释一下宏是什么,用官方一点的话来说就是:#define 机制包括了一个规定,允许把参数替换到文本中,这种实现通常称为宏(macro)或定义宏(definemacro)。啥意思呢?咱们用代码来看看:

#include <stdio.h>
#define MAX 100
#define ADD(X,Y) ((X)+(Y))
int main()
{
    int sum = ADD(10,20);
    printf("sum = %d\n", sum);
    sum = 10 * ADD(10,20);
    printf("sum = %d\n", sum);
    return 0;
}

大家看效果,是不是跟函数有点像,但又不是函数。之后深入了解,需要注意的是不要吝啬括号这个东西哦,这个非常重要,有细心的有伙伴发现我把括号都给去掉了嘛?重这样会造成问题的,比如说我把ADD()括号内部的值改一下,大家看效果:

结果是120????这是嘛肥事?不应该是300吗???还记得之前说的注意点吗?不要吝啬括号哦,按照宏的定义来解析:

int sum = 10 * ADD(10,20);
int sum = 10 * 10 + 20;//先算乘除加算加减

懂了,在上面加上括号不就好了。嗯对,大家不要吝啬括号哦。

???为啥还是120?我不是加了括号嘛?我们再来解析一下:

int sum = 10 * ADD(10,20);
int sum = 10 * (10) + (20);

啊这....那要怎么加括号呢?如果你想得到相加之后再乘以10是不是应该把这个相加的过程给括起来呢?

这样就没问题了。

指针

指针,没学之前听别人说过指针是C语言的灵魂,也是C语言的重点难点。简单介绍一下指针是什么吧,但是在学指针之前,我们要搞清楚一个东西,那就是内存。哈!内存,内存不就内存条吗?NONONO,如果就只是个内存条的话那就不用学这玩意了。其实内存是电脑上特别重要的存储器,计算机中所有程序的运行都是在内存中进行的。为了有效的管理内存空间,就把内存划分成一个个小的内存单元,每个内存单元的大小是1个字节。为了有效的访问内存单元,就给内存编号,这些编号就被称为该内存单元的地址。地址我们知道就比如我们住在亚洲中国34个省份的某一个城市某一个城区中。下面我画个跟内存有关的图,我假设我画的的是内存:

上面我写了一个比特位作为一个地址太浪费了而且管理起来也不方便,之前介绍过基本类型发现char类型是最小的而且大小只有一个字节,那么一个字节作为内存单元的大小这样不就好了吗?确实C语言规定内存单元的大小就是1个字节。大家多多少少都对内存有大概的了解了吧。接着我们说指针。

在计算机科学中,指针( Pointer )是编程语言中的一个对象,利用地址,它的值直接指向( points to )存在电脑存储器中另一个地方的值。由于通过地址能找到所需的变量单元,可以说,地址指向该变量单元。因此,将地址形象化的称为“指针"。意思是通过它能找到以它为地址的内存单元。

变量都会有地址,简单的来说,指针就是一个变量,而这个变量只是用来存储地址的。下面以代码为例子:

int main()
{
    int num = 10;
    #//取出num的地址
    printf("%p\n", &num);//打印地址,%p--以地址的形式打印
    return 0;
}

指针就是用来存储地址的,而且指针也是个变量,那么就可以定义指针变量来存储地址:

#include <stdio.h>
int main()
{
    int num = 10;
    int* p = #
    printf("%p\n",p);
    *p = 20;
    printf("%d\n",num);
    return 0;
}

我们看结果,第一个是00AFF9D8,第二个是20,咦我的num怎么被改变了呢?第一个是地址是因为指针变量存的是num的地址,因为p变量指向的是num,也就是p变量通过地址找到了num,*解引用操作符,间接访问了num,*p找到的就是num,这样num的值就被改变了。这只是整型指针,我们可以以此类推到其他类型,比如:

#include <stdio.h>
int main()
{
    char ch = 'w';
    char* p = &ch;
    *p = 'q';
    printf("%c\n",ch);
    return 0;
}

画个图大家应该可以更清楚一点:

p的值是地址哦,比如int* p = 20;它会被解析成:

int * p = 20 == 0x00000014;//20会被解析成地址 而地址是十六进制的

可以看到int* p = 某个值,某个值都会被解析成地址的形式。大家要注意哦!

指针的大小

我们知道了指针是个用来存放地址的变量,那指针变量的大小是多少呢?

#include <stdio.h>
int main()
{
    printf("int* = %d\n",sizeof(int*));
    printf("char* = %d\n",sizeof(char*));
    printf("short* = %d\n",sizeof(short*));
    printf("float* = %d\n",sizeof(float*));
    printf("boule* = %d\n",sizeof(double*));
    return 0
}

我们在不同的平台上运行一下,32位:

我们发现在32位平台上,所有类型指针都是4字节。

64位:

我们看到在64位平台上都是8个字节。我们可以得出结论:

指针大小在32位平台上是4个字节,64位平台上是8个字节。

结构体

我们在介绍一本书的时候会怎样描述呢?书名+价格+书号+出版社等等。对吧,或者描述一个学生,学生有姓名、年龄、性别、学号等。这些在C语言中又是怎样描述的呢?这就牵扯到了C语言中的结构体了,结构体可以让C语言有能力描述这些复杂的类型。那我们用结构体来描述一个学生类型:

struct Student
{
  char name[10];//名字
  int age;		//年龄
  char sex[5];	//性别
  char id[19];	//学号
};

结构体的定义方式:

struct 结构体名称
{
  类型 结构体成员1;
  类型 结构体成员2;
  类型 结构体成员3;
    .....
};

初始化并定义结构体变量方式位:

struct 结构体名称 结构体变量名 = {成员1赋值,成员2赋值,.....};

#include <stdio.h>
//定义一个结构体类型 结构体名称Student
struct Student
{
  char name[10];//名字
  int age;		//年龄
  char sex[5];	//性别
  char id[19];	//学号
};
int main()
{
    //定义一个结构体Student类型的变量 并初始化
    struct Student s = {"小顾",20,"男","201717101"};
    printf("姓名:%s\n",s.name);
    printf("年龄:%d\n",s.age);
    printf("性别:%s\n",s.sex);
    printf("学号:%s\n",s.id);
    return 0;
}

也可以使用 - >操作符来访问哦:

#include <stdio.h>
//定义一个结构体类型 结构体名称Student
struct Student
{
  char name[10];//名字
  int age;		//年龄
  char sex[5];	//性别
  char id[19];	//学号
};
int main()
{
    //定义一个结构体Student类型的变量 并初始化
    struct Student s = {"小顾",20,"男","201717101"};
    struct Student* p = &s;
    printf("姓名:%s\n",s->name);
    printf("年龄:%d\n",s->age);
    printf("性别:%s\n",s->sex);
    printf("学号:%s\n",s->id);
    return 0;
}

效果跟上图是一样的大家可以试试。

同样的跟修改变量类似,也可以对结构体变量中的值操作,改变结构体成员的值。

#include <stdio.h>
//定义一个结构体类型 结构体名称Student
struct Student
{
  char name[10];//名字
  int age;		//年龄
  char sex[5];	//性别
  char id[19];	//学号
};
int main()
{
    //定义一个结构体Student类型的变量 并初始化
    struct Student s = {"小顾",20,"男","201717101"};
    struct Student* p = &s;
	printf("%-20s\t%-20s\t%-20s\t%-20s\n","姓名","年龄","性别","学号");
    printf("%-20s\t%-20d\t%-20s\t%-20s\n", s.name, s.age, s.sex, s.id);
    printf("%-20s\t%-20d\t%-20s\t%-20s\n", p->name, p->age, p->sex, p->id);
    s.name = "小明";
    printf("修改后的姓名:%s\n",s.name);
   	p->age = 19;
    printf("修改后的年龄:%d\n", p->age);
    return 0;
}

代码效果图:

大家可以看到效果正如我们所改动的一样。但是呢,为啥没有改变名字呀?因为姓名是字符串数组是不可以直接改变的,得用字符串函数来改变,给大家演示一下吧,既然需要字符串函数,那就需要引入相对应的头文件string.h,字符串函数其中的strcpy函数,用来拷贝字符串的。

有关于字符串操作函数,放到后面再说。


感谢大家的观看!谢谢大家!!!本人也只是菜鸟一枚,有哪些写的不好的地方,讲的不明白的地方大家都可以指点指点,谢谢大家!本人也没学的好,所以也难免有些地方没有写好,还望大家体谅体谅,谢谢大家!本人写这篇博客也是为了分享一下自己的理解,有一起学习的小伙伴可以互相支持一下哦!这篇也只是初步的认识一下C语言,认识C语言都有哪些内容。好了谢谢大家的观看!!!阿里嘎多扩散一码事!!!

原文链接:http://www.cnblogs.com/L-wk/p/15670455.html

 友情链接: NPS