文章目录

  • 前言
  • 一、什么是C语言(了解)
  • 二、第一个C语言程序
  • 三、数据类型
  • 四、变量、常量
    • 4.1变量
      • 4.1.1定义变量的方法
      • 4.1.2变量的分类
      • 4.1.3变量的使用
      • 4.1.4变量的作用域和生命周期
    • 4.2常量
      • 4.2.1 字面常量
      • 4.2.2. const 修饰的常变量
      • 4.2.3. #define 定义的标识符常量
      • 4.2.4. 枚举常量
  • 五、字符串+转义字符+注释
    • 5.1字符串
    • 5.2转义字符
    • 5.3注释
  • 六、选择语句
  • 七、循环语句
  • 八、函数
  • 九、数组
    • 9.1数组定义
    • 9.2数组的下标
  • 十、操作符
    • 10.1算术操作符
    • 10.2移位操作符
    • 10.3位操作符
    • 10.4赋值操作符
    • 10.5单目操作符
    • 10.6关系操作符
    • 10.7逻辑操作符
    • 10.8条件操作符
    • 10.9逗号表达式
  • 十一、常见关键字
    • 11.1typedef
    • 11.2static
  • 十二、#define定义常量和宏
  • 十三、指针
    • 13.1指针使用示例
    • 13.2指针的大小
  • 十四、结构体
    • 14.1结构体是什么?
    • 14.2初识结构体(我们以学生小明为例)
      • 14.2.1创建学生结构体
      • 14.2.2对小明进行描述
    • 14.3结构体指针
      • 14.3.1结构体指针的使用
  • 后记

前言

这里是C语言专栏重制版,笔者将定期更新C语言相关文章,让读者快速上手,并深入理解C语言。笔者在之前也有一些C语言和JAVA基础与进阶相关文章,感兴趣的读者可自行查看。本专栏是面向零基础或者刚刚入门又或者准备系统复习一下C语言的读者。也是为笔者本人考研的C语言打一个铺垫,未来与诸君共勉。希望大家读完文章都能有所收获。


提示:以下是本篇文章正文内容,下面案例可供参考

一、什么是C语言(了解)

二、第一个C语言程序

我们的C语言中,main函数就是程序的入口,一个工程中main函数有且只有一个。

我们可以在main函数中写一些我们需要的程序代码。

#include <stdio.h>
int main() {//main函数是C语言程序的入口,有且只有一个main函数printf("hello world");//表示打印字符串hello worldprintf("1\n");//\n表示换行printf("2");return 0;
}

运行结果如下:

我们代码中hello world中没有使用\n,也就是换行,所以1会跟在hello world后面
而1使用了换行,所以2会到下一行进行打印。

三、数据类型

数据有很多类型啊,
比如笔者的姓氏字母是w,这个w就是一个字符类型
比如笔者175cm,这个175就是一个整形(你可以理解为一个整数)
再比如笔者69.5kg,这个69.5就是一个浮点型(一个小数)

可能会有同学问:“为什么叫浮点型?”
解释如下:69.5=6.95 * 10=0.695 * 100
如果采用科学计数法,小数点是会上下浮动的,所以叫浮点型

C语言中的数据类型如下:

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

而不同数据类型所占的空间大小也是不同的
我们可以用sizeof( )这个操作符进行测试,比如sizeof(char)表示char类型的大小

int main() {//%d打印有符号数//sizeof()表示...的大小printf("%d\n",sizeof(char));//1字节(byte)printf("%d\n", sizeof(short));//2printf("%d\n", sizeof(int));//4printf("%d\n", sizeof(long));//4//c语言规定sizeof(long)>=sizeof(int)即可//在32位下sizeof(long)是4//在64位下sizeof(long)是8printf("%d\n", sizeof(long long));//8printf("%d\n", sizeof(float));//4printf("%d\n", sizeof(double));//8int age = 21;//age在内存上分配4字节的空间char ch = 'w';//ch在内存上分配1字节的空间float weight = 69.1;return 0;
}

运行结果如下

我们这里打印了一堆数字,那这些数字的单位是什么?
这里的单位是字节(byte)

我们计算机中的单位如下:

bit //最小单位
byte
kb
mb
gb
tb
pb

我们计算机能够识别的是二进制,也就是0和1
而这些0和1,一个0(或1)所占的一个空间就是1比特位(bit)

换算关系如下

1byte=8bit
1kb=1024byte
1mb=1024kb
1gb=1024mb
1tb=1024gb
1pb=1024tb

四、变量、常量

生活中的有些值是不变的(比如:圆周率,性别,身份证号码,血型等等)
有些值是可变的(比如:年龄,体重,薪资等等)。

不变的值,C语言中用常量的概念来表示,变得值C语言中用变量来表示

4.1变量

4.1.1定义变量的方法

类型名 变量名 =值

举例如下:

int age = 150;
float weight = 45.5;
char ch = 'w';

4.1.2变量的分类

变量分为两类:
1.局部变量
在大括号{ }内部定义的变量就是局部变量

2.全局变量
在大括号{ }外部定义的变量就是全局变量

int c = 99;//全局变量,在main函数外,没有{}限制它
int main() {int a = 100;//局部变量,在main函数一个范围内,有{}限制printf("%d", a);//100  return 0;
}

运行结果为100

注意:局部变量就是在main函数内定义的变量是错误的
举个例子:

int c = 99;//全局变量
void test() {//test是一个没有返回值的函数,这个后面讲int b = 10;//b也是一个局部变量,且不是在main函数内
}
int main() {int a = 100;//局部变量,在main函数一个范围内,有{}限制printf("%d", a);//100  return 0;
}

上面的b,是在test函数里面定义的,它也是一个局部变量。

需要注意的是如果局部变量和全局变量名字相同,局部变量优先

int a = 99;//全局变量
int main() {int a = 100;//局部变量printf("%d", a);//100 //如果局部变量和全局变量名字相同,局部变量优先return 0;
}

4.1.3变量的使用

int main() {int n1 = 0;int n2 = 0;scanf("%d %d", &n1, &n2);//输入两个数,一个放到 n1的地址处,一个放到 n2的地址处int sum = n1 + n2;printf("%d", sum);
}

比如我这里输入两个数:520和1314,那么就会自动输出1834

4.1.4变量的作用域和生命周期

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

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

生命周期
变量的生命周期指的是变量的创建到变量的销毁之间的一个时间段
3. 局部变量的生命周期是:进入作用域生命周期开始,出作用域生命周期结束。
4. 全局变量的生命周期是:整个程序的生命周期。

4.2常量

C语言中的常量和变量的定义的形式有所差异。

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

4.2.1 字面常量

//字面常量
int main() {//直接写出来的值,就是字面常量3.14;100;"abc";//字面的字符串常量
}

4.2.2. const 修饰的常变量

我们知道变量是可以改变的,如下示例

int main() {int n = 1;n = 2;//用2赋值n,也就说变量n从1变成了2printf("%d", n);//打印2
}

但是如果在变量前加一个const呢?
大家可以对比一下我加const前和const后编译器的变化

加const前:没有报错信息

加const后:有报错信息

他提示你表达式必须是可修改的左值,也就是说被const修饰后的变量就不可修改了

特别说明:

在C99标准前,数组的大小只能是常量。笔者这里用的编译器是VS2017,对C99标准支持的不是很好,所以这里报错了。

那么我们是不是在n之前加一个const,就不会报错呢?

如上图,加了const后依旧会报错,这就说明了一件事:
被const修饰的变量只是不能改变了,但是他本质仍然是个变量,不是常量

4.2.3. #define 定义的标识符常量

#define x 100 //define也就是字面意思定义,
//这里是定义x 为100,注意是没有等于号的
int main() {printf("%d", x);return 0;
}

同样的,#define出来的量也是不能修改的,你要是修改就会如下图报错

注意:这里的define定义的值是可以做数组大小的,它是一个常量,请大家区别于const
可以看到,下图是没有报错的。

4.2.4. 枚举常量

枚举就是一 一列举的意思。

比如我们现在把一些值一 一列举出来:
性别:男、女
三原色:红、绿、蓝

enum Day//enum Day是一种类型
{Mon,//周一到周日这些叫枚举的可能取值,也叫枚举常量Tue,Wed,Thur,Fri,Sat,Sun,
};

上述代码定义的enum Day是一种枚举类型,{}中内容是枚举类型的可能取值,也可叫作枚举常量。
这些枚举常量都是有值的,默认从0开始,依次往后递增1,如果你愿意赋值也是可以的。
代码如下:

#include<stdio.h>
enum Color//enum Color是一种类型
{red,green,blue
};
int main()
{printf("%d\n", red);printf("%d\n", green);printf("%d\n", blue);
}

不赋初值打印0, 1,2

#include<stdio.h>
enum Color//enum Color是一种类型
{red=2,green,blue=6
};
int main()
{printf("%d\n", red);printf("%d\n", green);printf("%d\n", blue);
}

赋初值后打印初值内容,如果你有一个没有赋值,会紧接上一个赋值内容+1
,这里green并没有赋值,但我们知道red是2,按照枚举规则,green会在red的基础上加1变成3,
注意,blue是有赋值的,所以blue不需要在green的基础上加1,blue就是6

注意:因为是枚举{}内的叫作枚举常量,常量是不能改的,比如你在main函数里进行赋值操作,red=9;这个铁铁的会报错,一定要区分枚举初始化和赋值的区别。

五、字符串+转义字符+注释

5.1字符串

“i love china”

这种由双引号(Double Quote)引起来的一串字符称为字符串字面值(String Literal),
或者简称字符串

如果将来读者学了Java会涉及到字符串类型String,但是C语言中是没有字符串类型的。
虽说C语言没有字符串类型,但是我们也是可以把字符串放到字符数组里面进行存储

示例代码如下:

int main() {char arr1[] = "abc";//把字符串abc放到数组arr1中//数组在创建过程中,如果后面进行了初始化,可以不指定大小//大小会根据后面初始化内容来自己变化return 0;
}

大家可以按f10进行调试(如果f10不起作用,按fn+f10)


可以看到我们arr1里面是存放了’a’ ‘b’ ‘c’ ‘\0’
可我们明明代码中只写了"abc"啊

其实代码中是隐藏了一个‘\0’作为结束标志
重点:字符串的结束标志是一个 \0 的转义字符。
在计算字符串长度的时候 \0 是结束标志,不算作字符串内容。

如果我们用大括号的方式往字符数组里面放,会不会有’\0’呢?

int main() {char arr2[] = { 'a', 'b', 'c' };char arr3[] = { 'a', 'b', 'c', '\0' };return 0;
}

我们调试一下看:可以看到arr2中没有主动放‘\0’就不会有

我们再来试着打印一下arr1、arr2、arr3看它们之间到底有什么样的联系

int main() {char arr1[] = "abc";//把字符串abc放到数组arr1中//数组在创建过程中,如果后面进行了初始化,可以不指定大小//大小会根据后面初始化内容来自己变化char arr2[] = { 'a', 'b', 'c' };char arr3[] = { 'a', 'b', 'c', '\0' };printf("%s\n", arr1);//%s以字符串形式打印,后面会学 %c以字符形式打印,注意区别printf("%s\n", arr2);printf("%s\n", arr3);return 0;
}


可以看到arr2打印了abc然后一堆乱码…

原因如下:
我们是以%s的形式打印字符串,遇到\0后就知道是结束了,就不再打印了
但是你arr2里面没有\0,打印完abc后没有结束标志啊,
编译器就一直往后找,一直打印,直到找到\0然后才结束打印

因为我们“abc”会默认后面有一个\0,所以arr1打印没有问题
但是用{ }来写就没有默认添加\0了,你不自己添加,编译器就傻傻的找,直到找到\0

计算字符串长度:
strlen函数:从字符串开头往后找,找到\0结束

#include <stdio.h>
#include <string.h>
//要用库函数strlen,需要包含头文件string.h
int main() {char arr1[] = "abc";char arr2[] = { 'a', 'b', 'c' };char arr3[] = { 'a', 'b', 'c', '\0' };int len1 = strlen(arr1);//库函数strlen是用来求字符串长度的int len2 = strlen(arr1);int len3 = strlen(arr1);printf("%d\n", len1);//3printf("%d\n", len2);//随机值printf("%d\n", len3);//3return 0;
}

arr1中是有\0的,strlen从前往后数,直到\0结束(\0不计算),也就是数abc一共3个,所以打印了3
arr3和arr1是同理的

问题就是arr2为什么是随机值?
原因也很简单,因为我们没有给它安一个\0,而内存是连续的
我们strlen向后一直找,直到随机找到一个\0结束,所以它打印的值也是随机的

5.2转义字符

转义字符,也就是转变字符原有的意思
举例如下:

假如我们要在屏幕上打印一个目录: c:\code\test.c
我们要怎么写代码呢?

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

上述代码运行结果如下

这里就不得不提一下转义字符了。转义字符顾名思义就是转变意思。
下面看一些转义字符。


由上图可以知道了,如果我们想防止一个 \ 和后面的字母构成转义字符
我们在 \ 前再加一个 \ 即可

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

这下就可以正常打印了

关于转义字符,大家不要去背它的功能,因为一般也就用几个,比如\n是换行,稍微了解一下即可。

我们下面介绍两个比较重要的转义字符\ddd和\xdd

\ddd:

#include <stdio.h>
int main()
{printf("%c", '\162');//以字符形式打印'\162'//\162表示八进制数162,转换成十进制为114//打印出rreturn 0;
}


162转换成八进制是1 * 8^2+6 * 8^1 +2 * 8 ^ 0=64+48+2=114
114正好对应我们的ASCII码表的114位

注:如果你的八进制数只有两位,比如\71,你写成\071也是可以的,最后结果一样

#include <stdio.h>
int main()
{printf("%c\n", '\71');printf("%c\n", '\071');//八进制71转换成十进制是7*8^1+1*8^0=57,对应ascii码的9return 0;
}

\xdd:

int main()
{//16进制数由0-9和 abcdef(分别对应10-15)组成printf("%c\n",'\x5a');
}

16进制的5a转换成10进制是5*161+a*160=80+10=90
也就是ASCII码表中的Z

最后我们来看一道笔试题

//程序输出什么?
#include <stdio.h>
int main()
{printf("%d\n", strlen("abcdef"));printf("%d\n", strlen("c:\test\328\test.c"));return 0;
}


6的话很好理解,一共6个字符,到隐藏的\0结束

14怎么理解呢?
我们前面c:一共2个
然后到了\t,是一个转义字符,它是算1个
再往下est一共3个
再往下

这时有同学可能会抢答了:\328你之前讲过,是一个八进制数
我想问:“八进制数最高是多少?”
我们十进制数最高是9啊,同理,八进制数最高是7啊,怎么会出现8呢,
所以这里\32是一个 8是另外一个 ,一共2个

再往下\t 1个
最后est.c一共5个
综上一共14

5.3注释

  1. 代码中有不需要的代码可以直接删除,也可以注释掉
  2. 代码中有些代码比较难懂,可以加一下注释文字
#include <stdio.h>
int Add(int x, int y)
{return x+y;
}
/*C语言风格注释
int Sub(int x, int y)
{return x-y;
}
*/
int main()
{//C++注释风格//int a = 10;//调用Add函数,完成加法printf("%d\n", Add(1, 2));return 0;
}

注释有两种风格:
C语言风格的注释 /xxxxxx/
缺陷:不能嵌套注释

C++风格的注释 //xxxxxxxx
可以注释一行也可以注释多行

六、选择语句

生活中处处面临选择,比如大学里有的同学准备考研了,所以他努力学习;也有的同学天天混日子,所以他毕业就回家种地了
C语言中也有选择,不同的选择,对应不同的选择结果。我们常常通过下面的代码进行选择

if(条件)
{...
}
else
{...
}

举个例子:

int main()
{int a = 0;printf("你大学有好好学习吗?请输入1/0\n");scanf("%d", &a);if (a == 1) {printf("你好好学习了,你将拥有一个光明的未来");}else {printf("不好好学习就给老子爬");}return 0;
}

可以看到我们这里不同的输入,控制台会给出不同的结果,这就是选择

关于选择if还有一些细节,以及switch语句,这两个我会放在《从零开始C语言精讲篇2:分支与循环》
进行详细的讲解,这里的选择与循环只是做一个入门工作

七、循环语句

循环也是我们日常学习中经常用的一个知识点
比如我们学习总是日复一日,在达到一定程度之后,我们才能有所成就
循环也是这样,在经过反复操作后,到了一个触发条件,终于跳出循环继续往下执行代码。

C语言循环有三种:for循环、while循环、do while循环,我们这里做一个入门只介绍最简单的while循环,
for循环和do while循环我会放在《从零开始C语言精讲篇2:分支与循环》进行详细的讲解

while循环的语法如下:当满足执行条件时,执行{ }内的代码

while(执行条件)
{...
}

举例如下:

int main()
{//比如我们假定写了20000行代码就可以出师了int line = 1;//标记我们写的代码行数while (line < 20000) {printf("我在写第%d行代码\n", line);line++;}printf("20000行代码已写完,我出师啦!");return 0;
}

八、函数

函数的优点就是简化一些重复的代码,当你写了一个函数之后,你下次想要启用和这函数相同的功能时,
你就可以直接调用函数,而不是再写一遍函数的代码

函数的基本语法:

返回值类型 函数名(参数1类型 参数1,参数2类型,参数2...)
{...
return 返回值
}

举个例子:我们现在实现一个简单的加法函数

int add(int x, int y) {//加法函数return x + y;
}int main() {int a = 0;int b = 0;printf("请输入你要进行加法的两个数:\n");scanf("%d %d", &a, &b);int c = add(a, b);//把两参数a b放入函数add中,会分别对应x和y,//通过函数add计算后返回一个值,我们用c来接收这个值printf("两数和为:%d", c);//如果你懒的写c这个值,你直接打印add(a,b)也是可以的//printf("%d", add(a, b));return 0;
}

九、数组

要存储1-10的数字,怎么存储?
C语言中给了数组的定义:一组相同类型元素的集合

9.1数组定义

int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };//定义一个整形数组,最多放10个元素

举个例子:

int main() {int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };int brr[10] = { 1,2,3,4,5 };//不完全初始化,剩下默认初始化为0return 0;
}

我们按f10进行调试,可以看到我们的数组可以存储相同的类型的一些元素,如果数组没有被放满,我们会以0进行填充

我们以后写代码经常会看见int arr[10]={0}这种初始化方法,
这句代码的意思和我写的int brr[10] = { 1,2,3,4,5 }是差不多的
只不过它是意思是只初始化第一个为0,然后剩下的全部默认初始化为0

9.2数组的下标

C语言规定每个数组元素都有一个下标,数组元素的下标是从0开始的,

推广一下,如果我数组大小不是10,而是n,那么我数组最后一位下标应该是n-1

举个例子:

int main() {int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };int brr[10] = { 1,2,3,4,5 };printf("%d\n", arr[2]);//下标为2,对应元素为数组第三个printf("%d\n", brr[0]);//下标为0,对应元素为数组第一个return 0;
}


如果你想打印数组的所有元素,也很简单,加上前面说的循环即可

int main() {int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };int i = 0;//标记数组下标while (i < 10) {//这里条件你写i<=9也可printf("%d\n", arr[i]);i++;}return 0;
}

有同学可能会说,如果我将来把数组元素个数改变了,那你那个while也要改,好麻烦啊
对于这个问题,我们引入了sizeof()来改进代码

int main() {int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };int i = 0;//标记数组下标int sz = sizeof(arr)/sizeof(arr[0]);//sizeof(arr)可以计算出数组arr总大小,//然后我们除以每个元素大小(每个元素大小都是和arr[0]一样的),就可以得到元素个数//比如我们这里整形数组arr总大小为40,每个整形大小为4,那么个数=40/4=10while (i < sz) {printf("%d\n", arr[i]);i++;}return 0;
}

十、操作符

10.1算术操作符

+ - * / %

加减乘没什么好说的,和我们数学上都是一样的,我们来说一说除"/“和求余”%"

关于除号“/”

int main()
{int a = 7 / 2;printf("%d\n", a);//如果除号两边都是整数,那么得到的一定是整数
}

肯定会有人有疑问,那如果我非要得到一个小数呢?
也很简单,你把除号两边任意一个数变成小数即可,比如7/2变成7.0/2或者7/2.0

int main()
{float a = 7.0 / 2;//因为得到是小数,所以我们这里用float类型的a接收printf("%f\n", a);//打印也要用浮点型 %f来打印
}


如果想精确保留n位,那么在%f那里修改成%.nf,比如我这里想精确两位小数

int main()
{float a = 7.0 / 2;printf("%.2f\n", a);//如果除号两边都是整数,那么得到的一定是整数
}

关于求余数“%”

int main()
{int a = 7 % 2;//%是求余的意思,比如这里7/2=3...1,那么7%2=1printf("%d", a);
}

10.2移位操作符

>> <<
int main()
{//移位运算符移动的是二进制位int a = 12;int b = a << 1;//a左移一位得到的数放到b中//ps:0000 1100和1100是一个数,左边不管你补多少0都一样的//12的二进制表示是:  0000 1100  //向左移动一位为:    0001 1000 向左移动,右边补0//将0001 1000转换为10进制=1*2^4+1*2^3+0+0+0=16+8=24printf("%d", b);return 0;
}

因为是初识c语言,我们这里只是稍微介绍一下,后面笔者会更新操作符详解,对这里的移位进行详细介绍

10.3位操作符

& ^ |


关于按位与“&”:就是把二进制对应位进行与运算,全1为1,有0为0

int main()
{int a = 3;int b = 5;int c = a & b;//a的二进制:0011//b的二进制:0101//按位与   :0001  也就是十进制的1printf("%d", c);return 0;
}

关于按位或“&”:就是把二进制对应位进行或运算,有1为1,全0为0

int main()
{int a = 3;int b = 5;int c = a | b;//a的二进制:0011//b的二进制:0101//按位或   :0111  也就是十进制的7printf("%d", c);return 0;
}

关于按位异或“^”:就是把二进制对应位进行异或运算,相同为0,相异为1

int main()
{int a = 3;int b = 5;int c = a ^ b;//a的二进制:0011//b的二进制:0101//按位异或 :0110  也就是十进制的6printf("%d", c);return 0;
}

10.4赋值操作符

= += -= *= /= &= ^=  |=    >>=   <<=

赋值运算符很简单,我们举一个例子,大家就会全部的了

int main()
{int a = 10;//初始化a = 45;//赋值a += 1;//表示a=a+1;//其余的也是同样的意思,只不过把+改成了其他运算符号printf("%d", a);return 0;
}

注:关于浮点型赋值

int main()
{float a = 1.23;a = 1.24;//直接写出的1.24这样的值在c语言中是默认double类型的//我们最好写1.24f,这样可以指定为float类型return 0;
}

10.5单目操作符

!           逻辑反操作
-           负值
+           正值
&           取地址
sizeof      操作数的类型长度(以字节为单位)
~           对一个数的二进制按位取反
--          前置、后置--
++          前置、后置++
*           间接访问操作符(解引用操作符)
(类型)       强制类型转换

单目操作符,就是操作数只有一个的操作符

! 逻辑反操作

int main()
{//!逻辑反操作int flag = 5;//c语言中用0表示假,非0表示真if (flag){printf("哈哈");}if (!flag) //flag为真,!flag为假,所以这里就不会打印嘿嘿{//也就是说,如果想打印嘿嘿,flag需要为0printf("嘿嘿");}return 0;
}

+ - 正值和负值

int main()
{int a = -10;a = -a;//也就是把a变成他的相反数,这没啥好说的printf("%d\n", a);//打印10int b = +a;//+正号,其实根本没有用,你不写这个正号又有什么影响呢==.==printf("%d", b);return 0;
}

sizeof 操作数的类型长度(以字节为单位)

int main()
{//sizeof()是一个操作符,不是函数!!!printf("%d\n", sizeof(int));//打印4//sizeof(int)表示计算int这个类型创建的变量所占内存空间大小int a = 1;printf("%d\n", sizeof(a));//打印4return 0;
}


再次重申:sizeof不是函数,sizeof不是函数,sizeof不是函数!!!

sizeof也可以用来计算数组元素个数

int main()
{int arr[10] = { 0 };int sz = sizeof(arr) / sizeof(arr[0]);printf("%d", sz);//打印10return 0;
}

~ 对一个数的二进制按位取反

//计算机识别的是二进制
//整数存放在内存中的是二进制的“补码”
//怎么表示整数的二进制?//规定:
//非负整数的原码、反码、补码相同
//负整数的原码、反码、补码需要计算,计算规则如下:
//原码:直接按照数字的正负转换成二进制就是原码
//反码:原码的符号位不变,其他位按位取反
//补码:反码+1
int main()
{int a = 0;//int是4字节,也就是32bit//a原码:00000000 00000000 00000000 00000000  非负数原反补相同//a反码:00000000 00000000 00000000 00000000//a补码:00000000 00000000 00000000 00000000printf("%d", ~a);//~a是对内存里的a进行计算的,而内存里存放的是a补码,//也就是对补码进行按位取反//取反后补码 11111111 11111111 11111111 11111111 //还原成反码 11111111 11111111 11111111 11111110//还原成原码 10000000 00000000 00000000 00000001//也就是-1int b = -1;//b原码:10000000 00000000 00000000 00000001 //b反码:11111111 11111111 11111111 11111110  原码的符号位不变,其他位按位取反//b补码:11111111 11111111 11111111 11111111  反码+1}

- - 前置、后置–
++ 前置、后置++
这个是很常用的操作,也是很简单的,我们进行一个前置++和后置++的讲解,读者也很快能了解前置- -和后置- -

int main()
{//前置++:先加后用//后置++:先用后加int a = 10;int b = a++;int c = 10;int d = ++c;printf("b=%d\n", b);printf("d=%d\n", d);return 0;
}


前置++就是,在变量进行加1前,先把这个变量用了
后置++就是,在变量进行加1后,在使用这个变量

比如a++和++a
两者相同的是:a最后都变成了a+1,也就类似于a=a+1
不同的是:如果进行赋值,第一个得到的值是a,第二个得到的值是a+1

前置- - 和后置- -也是同理

* 间接访问操作符(解引用操作符)
这个和取地址操作符&是一对的,我们放指针那里进行具体介绍,不然读者会云里雾里,这个可以暂时跳过。

(类型) 强制类型转换

int main()
{int a = (int)3.14;//3.14是浮点型,你想把它赋给整形的a,直接赋是会报警告的(精度丢失)//如果不想报警告,我们在3.14前加一个(int)表示把3.14的类型强制转换成整形printf("%d", a);return 0;
}

10.6关系操作符

>
>=
<
<=
!=   用于测试“不相等”
==      用于测试“相等”

举个例子:

int main()
{int a = (3 > 5);printf("%d\n", a);//3>5不正确,返回一个0给a,这里打印0if (3 != 5) {//判断3是否不等于5//如果不等于就进入ifprintf("3不等于5");}return 0;
}

10.7逻辑操作符

&&     逻辑与
||     逻辑或

这个和我们高中学的且与或是一样的
A && B :A和B都成立 则该式子成立
A || B:A和B有一个成立,则式子成立

举个例子:

int main()
{int a = 4;int b = 3;if (a > 3 && b > 4) {//逻辑与,两边都成立才算成立printf("呵呵\n");}if (a > 3 && a < 5) {printf("哈哈\n");}if (a > 3 || b > 4) {//逻辑或,两边有一个成立即可printf("嘿嘿\n");}return 0;
}

10.8条件操作符

exp1 ? exp2 : exp3
//exp1是否正确,如果正确则exp2,否则exp3

举个例子:

int main()
{int a = 3;int b = 2;int max=(a > b) ? a:b;//该式表示:a如果大于b,则把a赋给max,否则把b赋给maxprintf("%d", max);return 0;
}

10.9逗号表达式

exp1, exp2, exp3, …expN
int main()
{int a = 3;int b = 5;int c = 6;int d = (a -= 2, b = a + c, c = a - b);//由逗号隔开的表达式,就叫逗号表达式//逗号表达式会从左向右依次进行计算//整个表达式结果是最后一个表达式结果//这里a-=2,也就是a=1了//b=a+c=1+6=7//c=a-b=1-7=-6//所以这里d=-6printf("%d", d);return 0;
}

十一、常见关键字

auto  break   case  char  const   continue  default  do   double else  enum
extern float  for   goto  if   int   long  register    return   short  signed
sizeof   static struct  switch  typedef union  unsigned   void  volatile  while

常见关键字如上,我们这里进行一个简单的分类,和一些比较重要的介绍:

这些关键字不用死记硬背,我们多写点c语言基本也就认识了

注:
关键字不能自己创建
变量名不能是关键字

现在我们来介绍一些关键字:

11.1typedef

typedef 顾名思义是类型定义,这里应该理解为类型重命名。
举个例子:

int main()
{unsigned int a = 10;//表示无符号整数//但是每次都写unsigned int很麻烦//能不能有办法把unsigned int简写成uint这种呢?return 0;
}

我们如果嫌弃某个类型写起来很麻烦,我们可以用typedef进行重命名

typedef unsigned int uint;//把unsigned int重命名为uint
//除了unit你也可以改其他名字
int main()
{unsigned int a = 10;//表示无符号整数uint b = 10;printf("%d", b);//可以正常打印10return 0;
}

11.2static

在C语言中:
static是用来修饰变量和函数的
1. static修饰局部变量-静态局部变量

举个例子:

void test()
{int a = 1;a++;printf("%d ", a);
}
int main()
{int i = 0;while (i<10){test();i++;}return 0;
}

上面这段代码,我们打印的结果为10个2,因为我们while会进行10次循环,然后调用10次test函数,进入test函数后,我们的a是每次都要重新创建的,并且在出了函数之后就会销毁,所以每次都是2

如果我们在int a前面加一个static呢?结果会有变化吗?

void test()
{static int a = 1;a++;printf("%d ", a);
}
int main()
{int i = 0;while (i<10){test();i++;}return 0;
}


可以看到,我们这次的打印结果变为了2,3,4,5…11
为什么呢?因为我们加了一个static让a这个变量出了函数也不会销毁,下次再进入这个函数也不会重新创建a,也就是说我们每次a++都是对同一个a进行的操作。所以我们会看到2,3,4,5…11

小结:使局部变量出了自己的范围也不会被销毁,
其实是改变了局部变量的生命周期,但是作用域还是局部的

2.static修饰全局变量-静态全局变量

全局变量是具有外部链接属性的,我们在一个.c文件中的全局变量可以在另一个.c文件中使用

比如我现在在源.c文件中创建一个全局变量a,并赋值100



可以看到,在声明后,我们可以在test.c中正常打印全局变量a的值

但是如果全局变量加了static后,全局变量会失去外部链接属性,变成内部链接属性,也就是说,被static修饰的全局变量,只能在自己所在的.c文件中使用。


小结:全局变量本身具有外部链接属性,但是static修饰全局变量,会使全局变量失去外部链接属性,变成内部链接属性,所以static修饰的全局变量,只能在自己所在的.c文件中使用

3.static修饰函数-静态函数

对于函数来说,也是可以在A文件中定义,在B文件中使用的(需要声明)

比如我们在源.c文件中写一个add函数

在test.c文件中也是可以正常使用它的(需要声明)

但是函数被static修饰后,就不能被其他文件使用了(外部链接属性没了)

这里再次调用就会报错了

小结:static修饰函数,函数默认是具有外部链接属性的,但是被static修饰后,会使得函数失去外部链接属性,变成内部链接属性。所以static修饰的函数只能在自己的.c文件内部使用,而不能在其他文件使用。

十二、#define定义常量和宏

#define定义常量在4.2.3讲过,我们这里只做简单介绍

#include<stdio.h>
#define num 1000
int main()
{int a = num;printf("%d", a);
}

那#define 定义宏是什么意思呢?
宏和我们的函数很像啊
比如我们这里定义一个add的宏

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#define  add(x,y) (x+y)
int main()
{int a = add(3,4);printf("%d", a);return 0;
}


宏就相当于是替换式的工作,比如我们代码中#define add(x,y) (x+y)
那么我们在main函数中add(3,4);就会把add(3,4)替换成(3+4)

ps:宏和函数各自有优势,但是我本人写代码一般都是函数用的多,宏用的比较少

十三、指针

我们这里是初识c语言,我们指针只做一个初阶的理解,后面笔者会专门出一期指针的全面讲解

要学会指针,我们需要先了解一下内存
内存是电脑上特别重要的存储器,计算机中所有程序的运行都是在内存中进行的 。

为了有效的使用内存,就把内存划分成一个个小的内存单元,每个内存单元的大小是1个字节

为了有效的访问到内存的每个单元,就给内存单元进行了编号,这些编号被称为该内存单元的地址

13.1指针使用示例

每个变量都有它的地址,我们这里取一个变量的地址看一下

int main()
{int a = 1;printf("%p", &a);//&:取地址操作符
}

按f10进行调试,然后点击调试、窗口、内存

我们可以看到运行结果是00FAFBF8(你不一定和我一样)

我们在内存界面输入这个地址00FAFBF8或者&a(取出a的地址)
我们这里用用4列来看这个地址(一个内存单元1字节,然后int是4字节嘛)

我们用4列看地址是这样的

如果换成1列来看,可以看出来,我们的a虽然是占了4个内存单元,
但是&a(取出a地址)是算的第一个内存单元的地址

可能会有同学问:“我们&a是得到的a的地址啊,如果我想把这个地址存起来怎么存?”

我们把这个地址放到变量pa中,pa的类型为int*

int main()
{int a = 1;printf("%p", &a);//&:取地址操作符//&a取出的是a所占内存中4个字节中第一个字节的地址int* pa = &a;//我们把a的地址放到pa中,pa的地址叫int*//int*怎么理解?//*说明pa是个指针//*前面的int说明pa指向的变量是个int型return 0;
}

画个图来帮大家理解一下:

图中我们pa中存放的是a的地址,存放的这个地址我们也可以叫作指针(可以通过存放的地址找到a)

那么我们来做一个推广,对于char类型的a来说,我们用pa存放它的地址,
char*中的 * 表示pa是个指针,char表示pa指向的变量类型是char类型

int main()
{char a = 'A';char* pa = &a;return 0;
}

类似的,其他类型指针也是如此…

我们前面说过,可以通过指针找到它指向的对象,那怎么找呢?
需要用到“ * ”,注意,这里的 * 不是表示指针,是解引用的意思

解引用就是,我现在有一个地址pa,我在pa前加一个*,也就是 * pa
这个 * pa就相当于通过pa得到了a,简单理解,你就可以当*pa=a

打个比方,我去办身份证,然后我在警察局留了我的家庭住址(指针),然后如果以后警察需要找到我,就可以通过这个地址(指针),这个通过地址找我的过程就是解引用。解引用后就得到了我这个变量。也就是*pa=a

int main()
{int a = 1;int* pa = &a;//我们把a的地址放到pa中,pa的地址叫int*//int*怎么理解?//*说明pa是个指针//*前面的int说明pa指向的变量是个int型printf("%d\n", *pa);//对pa进行解引用,也就得到了a,就相当于*pa==a*pa = 2;printf("%d\n", a);//原先a是1,我们通过*pa=2进行了a变量值的改变return 0;
}

13.2指针的大小

我们指针是用来存放地址的,那是不是很容易理解指针大小就是地址的大小呢?

32位环境下:指针大小为4字节
64位环境下:指针大小为8字节

这里的指针大小和指针指向什么类型是无关的。

int main()
{printf("%d\n", sizeof(char*));printf("%d\n", sizeof(int*));printf("%d\n", sizeof(short*));printf("%d\n", sizeof(double*));printf("%d\n", sizeof(float*));return 0;
}

我们换成64位,把x86改成x64即可换成64位

可以看到,打印结果由4

十四、结构体

结构体它是一种组合类型,也是我们自己创造的类型,或者说自定义类型。结构体的关键字为struct

14.1结构体是什么?

结构体是一个c语言极其重要的板块,我们在现实生活中描述一个具体的事物时,总会列举这个事物的各个特征。举个例子,学生小明今年18岁,性别男,身高180.5cm,体重124.6kg。我们在描述小明时,对他的年龄、身高、体重做出了举例,我们用结构体来描述复杂对象(复杂类型),这便是结构体,它也是一种数据类型。

14.2初识结构体(我们以学生小明为例)

14.2.1创建学生结构体

代码如下(示例):

struct student
{int age ;char sex[5];float height;float weight;
};//声明一个学生类型

注意点:这里struct是结构体关键字,student是结构体名,结构体默认会有大括号用来存放结构体成员,大括号最后别忘记有分号。完成上述代码,我们也就完成了一个学生结构体类型的创建,那么接下来我们就可以用这个类型来创建变量了。再次强调,结构体也是一种数据类型,它可以像其他类型一样创建变量

14.2.2对小明进行描述

创建一个结构体变量:xiaoming 并对他进行初始化用struct student xiaoming={…};大括号中间成员用逗号隔开。要打印小明的某个特点,只需将xiaoming.成员名与相应格式对应,比如:xiaoming.age对应%d,xiaoming.sex对应%s
代码如下(示例):

#include <stdio.h>
struct student
{int age;char sex[5];float height;float weight;
};
int main()
{struct student xiaoming = {18,"男",180.5,124.6};printf("小明年龄:%d,性别:%s",xiaoming.age,xiaoming.sex);return 0;
}

ps:这里对小明这个变量的创建也可直接跟在结构体大括号后,代码如下:

#include <stdio.h>
struct student
{int age;char sex[5];float height;float weight;
}xiaoming={18,"男",180.5,124.6};
int main()
{printf("小明年龄:%d,性别:%s",xiaoming.age,xiaoming.sex);return 0;
}

打印出来的效果是一样的

14.3结构体指针

示例:我们仍用上一期的小明同学进行举例
这里struct student *p=&xiaoming *p确认了p的类型是一个结构体指针,而前面的struct student确定了p指向的xiaoming是struct student类型,这里我们再次强调:结构体也是一种数据类型!!!

#include <stdio.h>
struct student
{int age;char sex[5];float height;float weight;
}xiaoming={18,"男",180.5,124.6};
int main()
{struct student *p=&xiaoming;return 0;
}

14.3.1结构体指针的使用

1.通过小明同学的地址找到小明

换句话说,工作时,你只有p=&xiaoming,怎么通过p找到xiaoming呢?
我们在前面的c语言指针学习中知道,*已知 int pa=&a 要通过pa找到a,只需要 * pa,也就是对pa进行解引用。结构体也是一样的,*p就是xiaoming嘛,再通过xiaoming找到小明的各项信息,即(*p).成员名=xiaoming.成员名,通过(*p).age找到小明的年龄,其他成员名以此类推。

下面两行代码打印出的信息是一样的(示例):

#include <stdio.h>
struct student
{int age;char sex[5];float height;float weight;
}xiaoming = { 18,"男",180.5,124.6 };
int main()
{struct student *p = &xiaoming;printf("小明年龄:%d,性别:%s\n", xiaoming.age, xiaoming.sex);printf("小明年龄:%d,性别:%s\n",(*p).age, (*p).sex);return 0;
}

2.另一种优化书写方案

上一种方法是同学们常常能想到的方法,但在实际中不便于书写,c语言给我们提供了另一种方法由上文已知p指向xiaoming,怎么通过p来找到小明的年龄?直接使用p->age
注解:p是一个指针,它有一个指向关系,p->的意思就是p指向的那个对象,那很容易理解,p->是小明这个对象,那p->age就是小明的年龄了,其他成员名以此类推

代码如下(示例):
你会发现下面三行代码打印出的东西也是一样的

#include <stdio.h>
struct student
{int age;char sex[5];float height;float weight;
}xiaoming = { 18,"男",180.5,124.6 };
int main()
{struct student *p = &xiaoming;printf("小明年龄:%d,性别:%s\n", xiaoming.age, xiaoming.sex);printf("小明年龄:%d,性别:%s\n",(*p).age, (*p).sex);printf("小明年龄:%d,性别:%s\n",p->age, p->sex);return 0;
}

后记

本文主要是c语言新手快速入门的一篇文章,后续还会有各个知识点的详细讲解,
希望日后与读者共同进步,相互共勉。

从零开始C语言精讲篇1:初识C语言相关推荐

  1. 从零开始C语言精讲篇5:指针

    文章目录 一.指针是什么? 二.指针和指针类型 三.野指针 3.1野指针成因 3.2规避野指针的方法 四.指针运算 4.1指针+-整数 4.2指针-指针 五.指针和数组 六.二级指针 七.指针数组 总 ...

  2. 面向对象C#初级入门精讲(2)C#语言基础-徐照兴-专题视频课程

    面向对象C#初级入门精讲(2)C#语言基础-397人已学习 课程介绍         欢迎加入QQ群538724338提问,这样老师能及时看到并回复 [课程特色] 1.课程设计循序渐进.讲解细致.通俗 ...

  3. 宾夕法尼亚大学计算机专业研究生,宾夕法尼亚大学计算机工程研究生排名最新消息精讲篇...

    原标题:宾夕法尼亚大学计算机工程研究生排名最新消息精讲篇 宾大建立之初,大学是一所慈善学院.本杰明?富兰克林作为院校设立人,认为新的知识来自对现有资源最广泛的认识和最有创新的运用.这一思想指导着他的研 ...

  4. 《C语言精讲第八课》——数组,超级详细且容易理解

    <C语言精讲第八课>--数组,超级详细且容易理解 文章目录 <C语言精讲第八课>--数组,超级详细且容易理解 前言

  5. c语言国二资料,国二C语言精讲资料.doc

    国二C语言精讲资料.doc 第一章C语言基础知识 该章内容:本章是整个C语言最基础的部分:表达式是C语言的核心,本章会学习到不同的表达式. 学习方法:对关键的知识点一定要多背,把最基础的习题多做几遍, ...

  6. C语言初阶学习-----01初识C语言

    这是初识C语言,对有一定基础的同学帮助很大,看不懂的到以后对应的章节会详细介绍. 1.数据类型 char //字符数据类型 short //短整型 int //整形 long //长整型 long l ...

  7. 【云原生】4.3 DevOps 精讲篇——Sonar Qube

    CSDN话题挑战赛第2期 参赛话题:学习笔记 目录 一.前言 二.SonarQube 安装 1.介绍 2.安装 三.Sonar Qube基本使用 1.安装中文插件 2.Maven 代码检测 3.Son ...

  8. 【云原生】4.2 DevOps 精讲篇

    哈喽~ 大家好呀,这篇就来详细介绍 DevOps 以及需要安装什么软件.

  9. c语言27除以4,2014计算机等级二级C语言精讲习题及答案 4

    单项选择题 26.若有函数 Viod fun(double a[], int *n) {---} 以下叙述中正确的是 A. 调用fun函数时只有数组执行按值传送,其他实参和形参之间执行按地址传送 B. ...

最新文章

  1. sont表示元素在存入集合时进行了排序,数据遍历的结果是按某个排序规则输出的
  2. 逼疯懒癌:“机器学习100天大作战”正式开始!
  3. 【Android】3.0 第3章 百度地图及其应用--预备知识
  4. 详细解析WSAAsyncSelect模型
  5. AsyncTask的基础讲解
  6. 线性代数不深入,机器学习两行泪!
  7. c#endread怎么打印出来_C#教程之打印和打印预览
  8. [BJDCTF2020]Cookie is so stable
  9. 运筹学常考知识点总结一
  10. 国内国外最好的java开发论坛及站点 [转]
  11. Thinking in Java Reading Note(2.一切都是对象)
  12. java中final用法
  13. 搭建 Ruby 运行环境【Rails后端开发训练营】
  14. 1037u处理器搭载文件服务器,悦升IVB 赛扬1037U工控主板 满足多行业需求
  15. VS2010旗舰版安装步骤
  16. yeezy350灰橙_Yeezy 350V2 Grey Orange 侃爷椰子350 灰橙 特价
  17. 华为HCIP-DATACOM391-420(821)
  18. Python实现箱形图的绘制
  19. C#/.NET 通过代码打开浏览器
  20. 数据正常运行之后,突然遇见:CiteSpace will re-run the process and keep non-empty intervals only.问题

热门文章

  1. 《2021网络空间测绘年报》解读|公有云资产画像与风险度量
  2. 远程修改服务器数据库配置,远程修改服务器数据库配置文件
  3. word2007 无格式文本 选择性粘贴 快捷键 定制方法(转)
  4. 骁龙625 mt6765_vivo Y3官网上线:5000mAh电池+联发科MT6765
  5. iOS 16怎么升级?iPhone升级iOS 16测试版图文教程
  6. 剑网三体服服务器没显示,剑网3体验服安装说明及步骤 教你完成体服安装
  7. 触摸屏坏了有哪些现象_外屏坏了有哪些现象
  8. 天文望远镜与8K摄像机结合应用简谈
  9. 异常续--捕获--重载与重写--finally块
  10. mac关闭icloud_如何在Mac上使用(或禁用)iCloud优化存储