总言
C语言初步认识。
文章目录
- 总言
- 1、main函数的三种写法
- 2、常见数据类型:内置类型、自定义类型
- 2.1、内置类型总述
- 2.2、内置类型大小计算(32/64)
- 2.3、计算机基本存储单位
- 3、常量、变量
- 3.1、变量
- 3.1.1、变量的分类:全局变量、局部变量
- 3.1.2、变量的作用域和生命周期
- 3.1.3、extern声明外部符号
- 3.2、常量
- 3.2.1、常量种类
- 3.2.2、使用列举
- 4、字符串、转义字符
- 4.1、字符串与\0
- 4.1.1、是什么
- 4.1.2、为什么
- 4.1.3、strlen求字符串长度
- 4.2、转义字符
- 5、关键字
- 5.1、关键字列举与大致介绍
- 5.2、一些常用关键字介绍
- 5.2.1、typedef
- 5.2.2、register
- 5.2.3、static
- 5.2.4、#define定义常量和宏
- 6、指针
- 6.1、内存与地址
- 6.2、指针变量
- 6.3、指针变量大小
- 7、结构体
- 8、其它
- 8.1、操作符
- 8.2、变长数组
1、main函数的三种写法
1)、main函数的基本写法
一种相对老式的写法:
void main()
{
}
常用写法:
int main()
{
return 0;
}
以后会遇到的一种写法:
int main(int argc,char* argv[])
{
return 0;
}
说明: main函数是程序的入口,一个工程项目中有且仅有一个main函数。
2)、vs编译器下的scanf函数
#define _CRT_SECURE_NO_WARNINGS
VS编译器认为C语言提供的scanf
函数是不安全的,所以其提供了一个安全版本的scanf_s
。但我们不建议使用它,因为其不具有跨平台性。
如果要使用C中的scanf
,则需要在第一行加上上述那句定义。(也有其它解决方法)
关于scanf
函数不安全的一个举例说明:
int main()
{
char arr[5] = { 0 };
scanf("%s", arr);
printf("%s\n", arr);
return 0;
}
如上图,scanf并不关心数组是否越界问题,它仍旧把我们输入的非法字符串存储进去。此处报错是VS检测的。
2、常见数据类型:内置类型、自定义类型
2.1、内置类型总述
1)、常见数据类型
char //字符数据类型
short //短整型
int //整形
long //长整型
long long //更长的整型
float //单精度浮点数
double //双精度浮点数
1、是什么
上述这些类型是C语言提供的类型,也叫内置类型。
由我们自己创造的类型叫做自定义类型,将在后续学习。
2、为什么
类型的使用是为了创建各种数据量。
2.2、内置类型大小计算(32/64)
1)、32位下各数据类型的大小
演示代码如下:
#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;
}
sizeof
计算所得单位为字节。
以下是这些数据类型在X86(32位)环境下运行的结果:
1、C语言标准规定:
①sizeof(long) >= sizeof(int)
即可。
2)、64位下各数据类型的大小
#include <stdio.h>
int main()
{
printf(" %lld\n", sizeof(char));
printf(" %lld\n", sizeof(short));
printf(" %lld\n", sizeof(int));
printf(" %lld\n", sizeof(long));
printf(" %lld\n", sizeof(long long));
printf(" %lld\n", sizeof(float));
printf(" %lld\n", sizeof(double));
printf(" %lld\n", sizeof(long double));
return 0;
}
1、%lld:longlong int
,此处相对标准的写法是使用%u
。
2、如下图,64位下数据大小仍旧不变,这取决于编译器。
2.3、计算机基本存储单位
1)、计算机中基本存储单位介绍
bit:比特位
byte:字节
KB:千字节,Kilobyte
MB:兆字节,MByte
GB:千兆字节,Gigabyte
TB:太字节,Terabyte
PB:拍字节,Petabytes
……
数据存储单位:
3、常量、变量
3.1、变量
3.1.1、变量的分类:全局变量、局部变量
1)、是什么
局部变量:在函数内部或复合语句内部定义的变量。
全局变量:在函数外部定义的变量,也被称为外部变量。
2)、同名变量在全局、局部
#include <stdio.h>
int global = 2222;//全局变量
int main()
{
int local = 1111;//局部变量
//下面定义的global会不会有问题?
int global = 3333;//局部变量
printf("global = %d\n", global);
return 0;
}
原则:当局部变量和全局变量同名的时候,局部变量优先使用。
3.1.2、变量的作用域和生命周期
1)、总述
2)、举例
局部变量作用域举例
int main()
{
{
int a = 100;
printf("%d\n", a);//Ⅰ
}
printf("%d\n", a);//Ⅱ
return 0;
}
上述中,a的作用域范围在{ }
内,故Ⅱ
中printf打印失败。
全局变量作用域举例
int a = 100;//全局变量
int main()
{
{
printf("%d\n", a);//Ⅰ
}
printf("%d\n", a);//Ⅱ
return 0;
}
3.1.3、extern声明外部符号
1)、关于全局变量同工程内跨文件使用演示
如上图演示,在一个文件中定义的全局变量,extern声明后可在另一个文件中使用。
注意:int a = 100;
这句话是变量定义,extern int a;
只起到声明a是外部符号的作业。
int a = 100;
extern int a;
void test()
{
printf("test:%d\n", a);
}
int main()
{
test();
printf("main:%d\n", a);
return 0;
}
3.2、常量
3.2.1、常量种类
1)、C语言中的常量分类
C语言中的常量分为以下几种:
1. 字面常量
2. const 修饰的常变量
3. #define 定义的标识符常量
4. 枚举常量
3.2.2、使用列举
2)、举例演示
字面常量
int main()
{
//字面常量:
20;//整型常量
3.14;//浮点型常量
'a';//字符常量
"abcdefg";//字符串常量
int a = 10;//此处10即字面常量,此句话含义:用10这个字面常量为a赋值
char ch = 'w';//用字面常量'w'为ch变量赋值
return 0;
}
const修饰的常变量
const是C语言的一个关键字,被其修饰的变量具有常属性,即不可以被修改:
int main()
{
int num = 10;
num = 20;
printf("%d\n", num);
const int num2 = 30;
num2 = 40;
printf("%d\n", num);//error
return 0;
}
问题:上述被const修饰的常变量,其还是变量吗?
验证如下:
int main()
{
int arr[10] = { 0 };
int n = 10;
int arr1[n] = { 10 };//error
const int m = 10;
int arr2[m] = 10;//error
return 0;
}
m被const修饰后用于定义数组大小,仍旧报错,说明m具有常属性,但其不是常量。
#define 定义的标识符常量
#define M 100 //M=100
int main()
{
int a = M;
printf("%d\n", a);
return 0;
}
问题:既然它是常量,那么它能否用于数组定义大小?
回答:可以。
int arr[M] = { 0 };
枚举常量
enum是枚举类型的关键字,枚举常量的默认是从0开始,依次向下递增1的。
//举例
enum Sex
{
MALE,
FEMALE,
SECRET
}; //注意分号
int main()
{
//枚举常量演示
printf("%d\n", MALE);
printf("%d\n", FEMALE);
printf("%d\n", SECRET); //注:枚举常量的默认是从0开始,依次向下递增1的
return 0;
}
4、字符串、转义字符
4.1、字符串与\0
4.1.1、是什么
1)、什么是字符串?
①这种由双引号引起来的一串字符称为字符串字面值(String Literal),或者简称字符串。
"hello world!\n"
②字符串的结束标志:转义字符\0
,其不算作字符串内容。
2)、以数组为例,验证字符串尾部有\0
问:以下三个数组一样吗?
int main()
{
char arr[] = { 'a','b','c' };
char arr1[] = "abc";
char arr3[3] = { 'a','b','c' };
return 0;
}
此处说明字符串的尾部隐藏着一个\0
。
4.1.2、为什么
\0
作用验证:用于结束字符串。char arr[] = { 'a','b','c' };
中数组中无\0
,那么%s
字符串打印时,什么时候遇到\0
,什么时候结束,故会打印出乱码。
int main()
{
char arr[] = { 'a','b','c' };
char arr1[] = "abc";
printf("%s\n", arr);
printf("%s\n", arr1);
return 0;
}
对于上述乱码,我们可以主动放置'\0'
:
char arr[] = { 'a','b','c' ,'\0'};
4.1.3、strlen求字符串长度
strlen
求字符串长度,计算的是\0
之前出现的字符个数。
int main()
{
char arr[] = { 'a','b','c' };
char arr1[] = "abc";
char arr2[] = { 'a','b','c' ,'\0' };
printf("%d\n", strlen(arr));//随机值
printf("%d\n", strlen(arr1));
printf("%d\n", strlen(arr2));
return 0;
}
4.2、转义字符
1)、常见转义字符列举
转义字符,即转变原先字符的含义。
2)、转义字符演示
代码演示一:反斜杠
打印目录: c:\code\test.c
,以下代码输出结果为?
#include <stdio.h>
int main()
{
printf("c:\code\test.c\n");
return 0;
}
如何修改?
printf("c:\\code\\test.c\n");
故日后使用时,若有写路径的需求,需要注意。
代码演示二:\ddd和\xdd
笔试题引入:
//程序输出什么?
#include <stdio.h>
int main()
{
printf("%d\n", strlen("abcdef"));
printf("%d\n", strlen("c:\test\628\test.c"));
return 0;
}
重点知识:
\ddd:1~3个纯数字,表示一个八进制的的数字
\xdd:x+2个纯数字,表示一个十六进制的数字
1、\ddd
,此处的三个八进制字符不能超过十进制对应的ASCII码范围。
2、\ddd
,并不一定需要放满三个字符。
int main()
{
printf("%c\n", '\130');//八进制的130->十进制的88,所对应的ASCII码值:X
printf("%d\n", '\130');
return 0;
}
解释分析:
printf("%d\n", strlen("c:\test\628\test.c"));
返回上述笔试题,需要注意的是此处\628
,只有\62
是八进制转义字符,8超过范围,是单独的字符 (易错点)。
代码演示三:单双引号
问题:如何打印单、双引号?
printf("%c\n", '\'');
printf("%s\n", "\"");
5、关键字
5.1、关键字列举与大致介绍
1)、大致总览
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
2)、一些介绍
auto
:自动创建、自动销毁
int main()
{
{
auto int a = 10;//所有局部变量都有auto,只是现在很少使用
}
return 0;
}
break
:一般在switch语句、循环语句中使用
case
:一般用于switch语句
defualt
:用于switch语句
const
:修饰变量
continue
:用于循环
extren
:声明外部符号
goto
:用于分支循环
register
:用于定义寄存器变量
signed、unsigned
:有无符号
static
:修饰静态变量
typedef
:类型重命名
void
:空类型
volatile
:C++/Linux下讲解
5.2、一些常用关键字介绍
5.2.1、typedef
作用:类型重定义,即为类型重命名。
//将unsigned int 重命名为uint_32, 所以uint_32也是一个类型名
typedef unsigned int uint_32;
int main()
{
//观察num1和num2,这两个变量的类型是一样的
unsigned int num1 = 0;
uint_32 num2 = 0;
return 0;
}
5.2.2、register
寄存器关键字,建议将被其修饰的变量放到寄存器中。
寄存器(CPU)、高级缓存、内存、硬盘、网盘
上述存储,从右到左,空间变小,造价越高,速度越快。
int main()
{
int a = 10;
register int b = 20;
return 0;
}
5.2.3、static
0)、static整体介绍
在C语言中:
static是用来修饰变量和函数的
1. 修饰局部变量-称为静态局部变量
2. 修饰全局变量-称为静态全局变量
3. 修饰函数-称为静态函数
1)、static修饰局部变量
问:下述代码输出结果为?
void test()
{
int a = 5;
a++;
printf("%d ", a);
}
int main()
{
int i = 0;
while (i < 10)
{
test();
i++;
}
return 0;
}
局部变量,其创建在栈上,出了作用域被销毁,不会被保存。int a = 5;
每进入一次test
函数被创建一次。
假如我们为a加上static,如下:
输出结果是什么?
void test()
{
static int a = 5;
a++;
printf("%d ", a);
}
int main()
{
int i = 0;
while (i < 10)
{
test();
i++;
}
return 0;
}
static修饰局部变量,其存储在内存空间的静态区,改变了变量的生命周期,让静态局部变量出了作用域依然存在,到程序结束,生命周期才结束。
static int a = 5;
只在第一次进入函数时创建,出了局部范围不会被销毁,其后再次进入函数仍旧存在,直到整个程序结束。
2)、static修饰全局变量
static修饰全局变量,使得这个全局变量只能在本源文件内使用,不能在其他源文件内使用。
演示如下:
我们说全局变量能跨文件使用,而假如直接使用它则会报错。
需要使用关键字:extern,用来声明外部符号(非本文件中)。
那么假如我们为该全局变量加上static呢?
一个全局变量本来是具有外部链接属性的,但是被static修饰后,外部链接属性就变成了内部链接属性,只能在自己所在的源文件中使用,不能在其它文件内使用。
3)、static修饰函数
一个函数被static修饰,使得这个函数只能在本源文件内使用,不能在其他源文件内使用。
演示代码如下:
如果我们什么也不做强制去用,也能够输出,但事实上这是一种未定义行为。(因为编译器从main函数接口自上而下读取没有找到Add函数)
正确规范的写法是使用extern
声明一下:
同样的,假如我们为这个函数加上static
:
说明一个函数本来具有外部链接属性,但被static修饰后,外部链接属性就变成了内部链接属性,这个函数只能在自己所在的源文件中使用,其它文件中无法使用。
5.2.4、#define定义常量和宏
宏名一般约定俗成写为大写。
一个源文件,需要进过预编译、编译、汇编、链接过程,才能生成我们的可执行程序。
在预编译阶段会发生宏替换。
#define MAX 1000//define定义标识符常量
#define ADD(x, y) ((x)+(y))//define定义宏
#include <stdio.h>
int main()
{
int sum = MAX*ADD(2, 3);
printf("sum = %d\n", sum);
sum = 10*ADD(2, 3);
printf("sum = %d\n", sum);
return 0;
}
6、指针
6.1、内存与地址
1、为什么有16/32/64位机器?
例如,32位机器,有32根地址线(物理硬件层面),其通电后,电信号转换为数字信号,如1/0,那么就有:
0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000
0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0001
0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0010
0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0011
……
1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111
总共2^32byte,约为4G。我们将其依次编号,每个内存单元都有地址。
我们创建一个变量,变量在内存空间中有其对应地址。
#include <stdio.h>
int main()
{
int num = 10;
#//取出num的地址
//注:这里num的4个字节,每个字节都有地址,取出的是第一个字节的地址(较小的地址)
printf("%p\n", &num);//打印地址,%p是以地址的形式打印
return 0;
}
6.2、指针变量
假如需要把地址存储起来,如何定义变量呢?变量类型是什么?
因此就需要一个新的类型模式,即指针变量。
int num = 10;
int *p;//p为一个整形指针变量
p = &n;//p用于存储地址
int *
为此处p
的类型。
这里的*
作用是告诉我们:这个变量是指针变量,int
告诉我们p
指向的对象的类型是int
类型。
我们存储指针,是为了使用它,期待有朝一日通过指针直到其指向对象的地址,然而访问该对象。
使用如下:
#include <stdio.h>
int main()
{
int num = 10;
int *p = #
*p = 20;
return 0;
}
*p = 20;
,这里的*
是解引用操作符。通过p
中存放的地址,找到p
指向的内容。这里是将该指向内容num
修改为20。
6.3、指针变量大小
指针大小在32位平台是4个字节,64位平台是8个字节。
#include <stdio.h>
//指针变量的大小取决于地址的大小
//32位平台下地址是32个bit位(即4个字节)
//64位平台下地址是64个bit位(即8个字节)
int main()
{
printf("%d\n", sizeof(char *));
printf("%d\n", sizeof(short *));
printf("%d\n", sizeof(int *));
printf("%d\n", sizeof(double *));
return 0;
}
7、结构体
struct Stu
{
char name[20];//名字
int age; //年龄
char sex[5]; //性别
char id[15]; //ID
};
//打印结构体信息
struct Stu s = {"王朝", 21, "男", "20240101"};
//.为结构成员访问操作符
printf("name = %s age = %d sex = %s id = %s\n", s.name, s.age, s.sex, s.id);
//->操作符
struct Stu *ps = &s;
printf("name = %s age = %d sex = %s id = %s\n", ps->name, ps->age, ps->sex, ps-
>id);
8、其它
分支循环、数组、操作符
8.1、操作符
1)、算术操作符
+ - * / %
注意事项:
Ⅰ、整数除法和小数除法
Ⅱ、%为整数专用
int main()
{
printf("%d\n", 7 / 3);//整数除法:得到整数值,2……1,
printf("%f\n", 7.0 / 3);//小数除法,需要有一方为小数
printf("%f\n", 7 / 3.0);//小数除法,需要有一方为小数
return 0;
}
2)、逗号表达式
以下代码输出结果为?
int main()
{
int a = 3;
int b = 20;
int c = 0;
int d = (a -= 3, b += a, c = a - b, b = a - 4);
printf("a:%d b:%d c:%d d:%d\n", a, b, c, d);
return 0;
}
(a -= 3, b += a, c = a - b, b = a - 4)
整理为逗号表达式,表达式会从左到右依次进行,但取最后一个表达式的结果作为最终值。
3)、下标引用操作符
[ ]
,操作数:数组名、下标
int main()
{
int arr[10] = { 0 };
arr[5] = 9;
return 0;
}
4)、函数调用操作符
()
,操作数:函数名,参数
8.2、变长数组
牛客、力扣刷题时可使用。