1 C转义字符
1.1 C常用的的转义字符
代码:
#include <stdio.h>
int main()
{
// 1) \t: 一个制表位,实现对齐的功能
// 2) \n: 换行符
// 3) \\: 一个 \ 表示转义,第二个 \ 表示输出内容
// 4) \": 一个 " 表示转义,第二个 " 表示输出内容
// 5) \': 一个 ' 表示转义,第二个 ' 表示输出内容
// 6) \r: 一个回车
printf("北京\t天津\t上海\n");
printf("张三说:你好\n");
printf("hello, \\world\n");
printf("hello, \"world\n");
printf("hello, \'world\n");
// 分析:
// 1. 先输出:小明小黄你好
// 2. 然后输出:欧阳马匹你好
printf("小明小黄你好\r欧阳马匹\n");
return 0;
}
运行结果:
2 变量
变量是程序的基本组成单位。
2.1 变量的介绍
2.1.1 变量的概念
变量相当于内存中一个数据存储空间的表示,可看作是一个房间的门牌号,通过门牌号我们可以找到房间,而通过变量可以访问到变量的值。
2.1.2 变量的使用
// 声明变量
int num;
//变量赋值
num = 1;
//变量的使用
printf("num = %d\n", num);
//直接一步到位
int num_1 = 2;
printf("num_1 = %d\n" num_1);
#include <stdio.h>
int main()
{
int num = 1; //整数
double score = 98.5; //小数
char gender = 'M'; //字符
char name[] = "study c program"; //字符串
// 说明
/*整数输出 %d
小数输出 %f 保留多少位小数输出%.2f
字符输出 %c
字符串输出 %s
*/
printf("num = %d, score = %.2f, gender = %c, name = %s\n", num, score, gender, name);
// num = 1, score = 98.50, gender = M, name = study c program
return 0;
}
2.2 变量使用注意事项
1. 变量表示内存中的一个存储区域(不同的数据类型,占用的空间大小不一样);
2. 该区域有自己的名称和类型;
3. 变量必须先声明,后使用;
4. 该区域的数据可以在同一类型范围内不断变化;
5. 变量在同一个作用域内不能重名;
6. 变量三要素:变量名、值、数据类型。
2.3 变量的数据类型
2.3.1 数据类型一览图
2.3.2 数据类型的小结
注意:
1)在c中,没有字符串数据类型,使用的字符数组表示字符串;
2)在不同系统中,部分数据类型字节长度不一样,int有2或者4。
2.4 各种数据类型的存储大小
各种数据类型的存储大小与操作系统、系统位数和编译器有关。
c语言规定:short占用的存储空间不能多于int,long占用的存储空间不能少于int。
win64中各类型的存储大小:
#include <stdio.h>
int main()
{
printf("sizeof(char) = %d\n", sizeof(char)); //1
printf("sizeof(short) = %d\n", sizeof(short)); // 2
printf("sizeof(int) = %d\n", sizeof(int)); // 4
printf("sizeof(long) = %d\n", sizeof(long));// 4
printf("sizeof(long int) = %d\n", sizeof(long int)); //4
printf("sizeof(long long int) = %d\n", sizeof(long long int)); // 8
printf("sizeof(float) = %d\n", sizeof(float)); // 4
printf("sizeof(double) = %d\n", sizeof(double)); // 8
printf("sizeof(long double) = %d\n", sizeof(long double)); // 16
printf("sizeof(long long) = %d\n", sizeof(long long)); // 8
return 0;
}
linux64中各类型的存储大小:
#include <stdio.h>
int main()
{
printf("sizeof(char) = %d\n", sizeof(char)); //1
printf("sizeof(short) = %d\n", sizeof(short)); // 2
printf("sizeof(int) = %d\n", sizeof(int)); // 4
printf("sizeof(long) = %d\n", sizeof(long));// 8
printf("sizeof(long int) = %d\n", sizeof(long int)); //8
printf("sizeof(long long int) = %d\n", sizeof(long long int)); // 8
printf("sizeof(float) = %d\n", sizeof(float)); // 4
printf("sizeof(double) = %d\n", sizeof(double)); // 8
printf("sizeof(long double) = %d\n", sizeof(long double)); // 16
printf("sizeof(long long) = %d\n", sizeof(long long)); // 8
return 0;
}
2.5 整数类型
整数类型里面包含:整型(int)、短整型(short int)、长整型(long int)。
2.5.1 无符号类型unsigned
编译器默认在我们声明变量时,就缺省加上了signed这个关键字。当我们的变量需要变成无符号时,需要用到unsigned进行修饰。
无符号类型(unsigened)只能搭配char、int、short int、long int一起使用,用unsigned修饰后的数字,就是不带负号的数字,无法存放负数。
注:不能定义unsigned float和unsigned double类型,否则会报错
此表基于win64:
数据类型 | 名称 | 字节 | 值范围 |
short | 短整型 | 2 | -32,768 ~ 32,767 |
int | 整型 | 4 | -2,147,483,648 ~ 2,147,483,647 |
long | 长整型 | 4 | -2,147,483,648 ~ 2,147,483,647 |
unsigned short | 无符号短整型 | 2 | 0 ~ 65535 |
unsigned int | 无符号整型 | 4 | 0 ~ 4,294,967,295 |
unsigned long | 无符号长整型 | 4 | 0 ~ 4,294,967,295 |
2.6 浮点类型
浮点型分为单精度浮点型(float)和双精度浮点型(double)。
两者的都可以用来存放小数,区别在于双精度浮点型可以精确的小数位比单精度浮点型的多。
数据类型 | 名称 | 字节 | 范围值 |
float | 单精度浮点型 | 4 | 1.2E-38 ~ 3.4E+38(精确到小数点后6位) |
double | 双精度浮点型 | 8 | 2.3E-308 ~ 1.7E+308(精确到小数点后15位) |
说明一下:
1)关于浮点数在机器上中的存放形式的简单说明,浮点数 = 符号位 + 指数位 + 位数位,浮点数是近似值;
2)尾数部分可能丢失,造成精度损失。
2.6.1 浮点数使用细节
1)浮点型常量默认为double型,声明float型常量时,必须加上’f‘或'F';
2)浮点型常量有两种表示形式:
十进制数形式,如:5.12、512.0f、.512
科学计数法形式,如:5.12e2、5.12E-2
3)通常情况下,应该使用double类型,因为它比float类型更精准;
4)printf("d = %f\n", d); 在输出时,默认保留小数点6位。
#include <stdio.h>
int main()
{
// 浮点数默认为double类型,声明float类型时,须在后面加'f'或'F'
float f = 1.2; // 从double到float截断,1.2是double类型
float f_1 = 1.2f;
double d1 = 1.2;
double d2 = 5.12;
double d3 = .512; // 等价于0.512
double d4 = 5.12e2; // 等价于5.12 * (10^2) = 512
double d5 = 5.12e-2; // 等价于5.12 * (10^-2) = 0.0512
// f = 1.200000 f_1 = 1.200000 d1 = 1.200000 d2 = 5.120000 d3 = 0.512000 d4 = 512.000000 d5 = 0.051200
printf("f = %f f_1 = %f d1 = %f d2 = %f d3 = %f d4 = %f d5 = %f\n", f, f_1, d1, d2, d3, d4, d5);
double d8 = 1.19823294;
printf("d8 = %f\n", d8); // d8 = 1.198233
return 0;
}
2.7 字符类型
字符类型可以表示单个字符,字符类型是char,char是一个字节(可以是字母或者数字),多个字符称为字符串,在C语言中,使用char数组表示,数组不是基本的数据类型,而是构造类型。
数据类型 | 名称 | 字节 | 范围值 |
char | 字符类型 | 1 | -128 ~ 127 |
unsigned char | 无符号字符类型 | 1 | 0 ~ 255 |
2.7.1 字符类型使用细节
1)字符常量使用单引号('')括起来的单个字符。例如:char c1 = 'a',char c2 = '9';
2)C语言中还允许使用转义字符'\'来将其后的字符转为特殊字符常量。例如:char c3 = '\n'; (表示换行符);
3)在C语言中,char的本质是一个整数,在输出时,是ASCII码对应的字符;
4)可以直接给char附一个整数,在输出时,会按照对应的ASCII字符输出;
5)char类型时可以进行运算的,相当于一个整数,因为它都对应有Unicode码。
2.7.2 字符类型本质
1) 字符类型存储到计算机中,需要将字符对应的码值(整数)找出来:
存储:字符'a' --> 码值(97) --> 二进制(110001) --> 存储
读取:二进制(1100001) --> 码值(97) --> 字符'a' --> 读取
2)字符和码值的对应关系是通过字符编码表决定的(是规定好)
2.8 基本数据类型转换
2.8.1 自动类型转换
在C语言中,在进行赋值或者运算时,精度小的类型自动转换为精度大的数据类型,这个就是自动类型转换。
数据类型按精度大小排序为:
数据类型自动转换表:
2.8.2 自动类型转换细节说明
1)有多种类型的数据混合运算时,系统首先自动将所有数据类型转换成精度最大的那种数据类型,然后再进行计算(如int型和short型运算时,先把short转成int型后再进行运算);
2)若两种类型的字节数不同,转换成字节数大的类型,若两种类型的字节数相同,且一种有符号,一种无符号,则转换成无符号类型;
3)在赋值运算中,赋值号两边的数据类型不同时,赋值号右边量的类型将转换为左边量的类型,如果右边变量的数据类型长度比左边长时,将丢失一部分数据,这样会降低精度,丢失的部分按四舍五入进行舍入(只针对小数部分)。
#include <stdio.h>
int main()
{
int a = 1.98;
float f1 = 1.23f;
double d1 = 2.67232876;
f1 = d1;
a = d1;
printf("a = %d\n", a); // a = 2
printf("f1 = %f\n", f1); // f1 = 2.672329
return 0;
}
2.8.3 强制类型转换
将精度高的数据转换为精度小的数据类型。使用时要加上强制转换符(),但可能造成精度降低或溢出,格外要注意。
强制类型转换一般格式如下:
(类型名)表达式;
这种强制类型转换并不改变操作数本身。
2.8.4 强制转换的细节说明
1)当进行数据从 精度高 --> 精度低,就需要使用到强制转换;
2)强制转换只针对于最近的操作数有效,往往会使用小括号提升优先级。
#include <stdio.h>
int main()
{
double d = 1.982;
int num = (int)d; //这里注意,不是四舍五入,而是直接截断小数后部分
printf("num = %d, d = %f\n", num, d); // num = 1, d = 1.982000
// 强制转换只对最近的数有效,如果希望针对更多的表达式转换,使用()
int num1 = (int)3.5 * 10 + 6 * 1.5; // 想要这样35 + 9 = 44,但实际30 + 9 = 39
int num2 = (int)(3.5 * 10 + 6 * 1.5);
printf("num1 = %d, num2 = %d\n", num1, num2); // num1 = 39 num2 = 44
return 0;
}
2.8.5 案例总结
#include <stdio.h>
int main()
{
char c ='a';
int i = 5;
float f = .345f;
double d = 1.34;
double result = c + i + f; // float -> double 自动类型转换
char result1 = c + i + d; // 会有一个警告 double -> char 会有精度丢失
return 0;
}
3 指针入门
3.1 指针基本介绍
指针表示一个地址(存放的是地址)。
代码演示:
#include <stdio.h>
int main()
{
int num = 1;
// 定义一个指针变量
// 说明
// 1. int* 表示类型为指针类型
// 2. 名称ptr,ptr就是一个int* 类型
// 3. ptr指向了一个int类型的变量地址
int *ptr = #
// 输出一个变量的地址,使用格式是%p
// &num表示取出num这个变量对应的地址
printf("num = %d, &num = %p\n", num, &num); // num = 1, &num = 000000000061FE14
// *ptr 表示获取指针指向的值
// ptr 表示存放的地址
printf("*ptr = %d, ptr = %p\n", *ptr, ptr); // *ptr = 1, ptr = 000000000061FE14
// 指针变量,本身也有地址
printf("ptr的地址 = %p\n", &ptr); // ptr的地址 = 000000000061FE10
return 0;
}
内存演示:
3.2 指针细节
基本类型,都有对应的指针类型,形式为 数据类型* ,比如int对应的指针就是int*,float对应的指针类型就是float*,以此类推。
3.3 值传递和地址传递
3.3.1 值传递
定义:将变量指向的存储内容,在传递/赋值时,拷贝一份接收变量。
默认值传递的类型:基本数据类型(整数类型,小数类型,字符类型),结构体,共用体。
3.3.2 地址传递
定义:如果是指针,就将指针变量存储的地址,传递给接收变量,如果是数组,就将数组的首地址传递给接收变量。
默认传递地址的类型:指针,数组。
4 常量
4.1 基本介绍
1)常量是固定值,在程序执行期间不能改变。这些固定的值,又叫做字面量。
2)常量可以是任何的基本数据类型,比如整数常量、浮点常量、字符常量、字符串常量和枚举常量。
3)常量的值在定义后不能改变。
4.2 经常使用的常量
4.2.1 整数常量
整数常量可以是十进制、八进制或十六进制的常量。前缀指定基数:0x 或 0X 表示十六进制,0 表示八进制,不带前缀则默认表示十进制。整数常量也可以带一个后缀,后缀是 U 和 L 的组合,U 表示无符号整数(unsigned),L 表示长整数(long)。后缀可以是大写,也可以是小写,U 和 L 的顺序任意。
12 // 十进制
0123 // 八进制
0x5a // 十六进制
87 // 整数
97u // 无符号长整数
98l // 长整数
98ul // 无符号长整数
4.2.2 浮点常量
浮点常量由整数部分、小数点、小数部分和指数部分组成。可以使用小数形式或者指数形式来表示浮点常量。
3.1929393 // double常量
3.141592e-7 // 科学计数法
2.4f // float常量
4.2.3 字符常量
字符常量是括在单引号中(''),字符常量可以是一个普通的字符(例如 'a'),一个转义字符(例如 '\n')。
'2'
'd'
'\n'
'\r'
4.2.4 字符串常量
字符串常量是括在双引号("")中的,一个字符串包含类似于字符常量的字符:普通的字符、转义序列和通用的字符。
#include <stdio.h>
int main()
{
char str1[20] = "hello world";
char str2[100] = "hello \
animal";
printf("str1 = %s str2 = %s\n", str1, str2);
return 0;
}
4.3 常量的定义
定义常量的两种方式:
1)使用 #define 预处理器。
2)使用const关键字。
4.3.1 #define 预处理器
1)#define 预处理器定义常量的形式
#define 常量名 常量值
2) 代码演示
#include <stdio.h>
#define PI 3.14
int main()
{
// PI = 3.14159 这样是不可以的,因为PI是常量
double area;
double r = 1.2;
area = PI * r *r;
printf("area = %.4f\n", area); // area = 4.5216
return 0;
}
4.3.2 const 关键字
1)可以使用const声明指定类型的常量。
const 数据类型 常量名 = 常量值;
2)案例演示
#include <stdio.h>
// 说明
// 1. const是一个关键字,规定好,表示后面定义了一个常量
// 2. PI是常量名,即使一个常量,常量值就是3.14
// 3. PI因为是常量,因此不可以改变
// 4. const定义常量时,需要加分号
const double PI = 3.14;
int main()
{
// PI
double area;
double r = 1.2;
area = PI * r * r;
printf("area = %.4f\n", area); // area = 4.5216
return 0;
}
4.3.3 const和#define的区别
1)const定义常量时,带类型,#define不带类型;
2)const是在编译、运行的时候起作用,而#define是在编译的与处理阶段起作用;
3)#define只是简单的替换,没有类型检查。简单的字符串替换会导致边界效应;
4)const常量可以进行调试,#define是不能进行调试的,主要是预处理阶段就已经替换掉了,调试的时候就没它了;
5)const不能重定义,不可以定义两个一样的,而#define通过#undef取消某个符号的定义,再重新定义;
6)#define可以配合#ifdef、#ifndef、#endif来使用,可以让代码更加灵活,比如我们可以通过#define来启动或者关闭调试信息。
针对3、5两点的代码分析:
#include <stdio.h>
#define A 2
#define B (A + 1)
#define C A / B * 4
double b = 2 / 3 * 4;
// const不能重复定义,不可以定义两个一样的,而define通过undef取消某个符号的定义,在重新定义
const double PI = 3.14;
// const double PI = 3.14;
#define R 23
#undef R
#define R 14
int main()
{
// C = A / A + 1 * 4 = 2 / 2 + 1 * 4 = 5
// C = A / (A + 1) * 4 = 2 / (2 + 1) * 4 = 2 / 3 * 4 = 0
printf("C = %d\n", C); // C = 0
printf("b = %f\n", b); // b = 0.000000
return 0;
}
针对第6点的代码分析:
#include <stdio.h>
#define DEBUG
int main()
{
#ifdef DEBUG
printf("ok\n");
#endif
#ifndef DEBUG
printf("no ok\n");
#endif
return 0;
}
进阶使用:
/* TEST1 或 TEST2被定义,则选择执行printf1,否则执行printf2 */
#if defined TEST1 || defined TEST2
printf1(".....");
#else
printf2(".....");
#endif
/* TEST1 或 TEST2未被定义,则选择执行printf1,否则执行printf2 */
#if !defined TEST1 || !defined TEST2
printf1(".....");
#else
printf2(".....");
#endif
笔记整理,来自大佬视频!!!