目录
一、初始数据类型
1、初识数据类型
表示 | char | short | int | long | long long | float | double |
类型 | 字符类型 | 短整型 | 整形 | 长整型 | 更长整型 | 单精度浮点型 | 双精度浮点型 |
所占内存大小(字节) | 1 | 2 | 4 | 4 | 8 | 4 | 8 |
<2>内存间的关系
1B=8b(一字节等于八比特位) 1kb=1024B 1mb=1024kb 1gb=1024mb
1tb=1024gb 1pb=1024tb
<3>计算数据类型所占内存所用的函数
sizeof(数据类型)
如:
int main()
{
printf("%d\n", sizeof(char));
printf("%d\n", sizeof(int));
printf("%d\n", sizeof(long));
printf("%d\n", sizeof(short));
printf("%d\n", sizeof(long long));
printf("%d\n", sizeof(float));
printf("%d\n", sizeof(double));
return 0;
}
二、初识常量与变量
1、变量的定义方式
数据类型 变量名称 =变量初始值
如:
int main()
{
int a = 0;
char w = 'a';
float weight = 60.5;
printf(" %d\n %c\n %f\n", a, w, weight);
return 0;
}
变量分为全局变量和局部变量
全局变量:{}之外定义的变量 ,全局变量不初始化时,默认为0.
局部变量:{}之内定义的变量
☆当局部变量与全局变量的变量名称冲突时,局部变量优先
2、变量的使用
如:
#define _CRT_SECURE_NO_WARNINGS
int main()
{
int a = 0;
int b = 0;
int sum = 0;
scanf("%d %d", &a, &b);
sum = a + b;
printf("sum=%d\n", sum);
return 0;
}
3、变量的作用域和生命周期
变量的作用域
局部变量的作用域:变量所在的局部范围
全局变量的作用域:整个工程
☆如果所用的全局变量属于另外的源文件,则需用extern进行声明
变量的生命周期:变量建立与销毁之间的时间段
局部变量的生命周期:进入局部范围开始,出局部范围结束
全局变量的生命周期:整个程序的生命周期
4、常量
整型常量
字面常量 实型常量
常 字符常量
字符串常量
const修饰的常变量
量 #define定义的标识符常量
枚举常量
1)字面常量
①整型常量
有三种表示形式,分别是十进制整数、八进制整数、十六进制整数。
十进制整数:由正负号和0~9组成。
八进制整数:必须以0开头,后跟0~7组成。 例:0128,这个数字就是错误的,因为八进制没有8这个数字!!!
十六进制数:必须以0x或0X开头,后跟0~9、a~f或A~F
★只有十进制可以表示正负整数,而八进制和十六进制只能表示正整数。
②实型常量
有两种表示形式,分别是小数形式和指数形式。
小数形式:由数字和小数点组成。如:.3 3. 这两种表示方法均是合法的。
指数形式:由e或E连接前后两个数组成,要求e或E前面必须有数字,e或E后面必须为整数。如:1.2e2表示1.2×10²
③字符型常量:
有两种表示形式,分别时普通字符和转义字符。
普通字符:由一对单引号括起来的字符。一对单引号括起来一个空格是一个合法的字符,而只有一对单引号是非法的字符。
转义字符:由一对单引号括起来的以\开头后接一个或几个字符的字符序列。
转义字符 | 含义 |
\n | 换行 |
\t | 水平制表符 |
\a | 鸣铃 |
\b | 退格 |
\r | 回车 |
\v | 垂直制表符 |
\f | 走纸换页 |
\\ | \ |
\' | ' |
\" | " |
\? | ? |
\ddd | d是八进制数,1~3位的八进制数 |
\xhh | h是十六进制数,一或两位的十六进制数 |
\0 | 字符串的结束标志 |
④字符串常量
字符串是由一对双引号括起来的一组字符序列。只有一对双引号也是合法的,表示空串。
<1>字符串的结束标志是\0,\0也是字符串中的字符也占一个字节,但是求字符串长度时\0不参与运算。
<2>如果在程序中用双引号括起来一串字符,那么字符串后面隐藏了一个\0。如果在程序中字符串是由一个个由单引号括起来的字符组成的,那么后面的\0位置并不确定。
<3>求字符串长度的函数时strlen(),需要引用头文件,#include<string.h>
例程序如下:
#include <stdio.h>
#include<string.h>
int main()
{
char arr1[] = "abc";
char arr2[] = { 'a', 'b', 'c' };
printf("%d\n",strlen(arr1));
printf("%d\n", strlen(arr2));
return 0;
}
所求的结果中,由arr1打印出来的值为3,而由arr2打印出来的值为随机值,因为不知道\0的位置。
2)const 修饰的常变量
在定义的变量前用const修饰一下,就可以使变量目前的值不可变,即有常量的属性。
如:
#include <stdio.h>
int main()
{
int a = 9;
const int b = 3;
a = 99;
//b = 33;
printf("%d\n",a);
printf("%d\n", b);
return 0;
}
代码中,a可以随时进行改值,而b的值不能被改变,一旦要改变b的值,程序就会出现错误。
而之所以称为常变量是因为,它本身是变量只不过具有了常属性。
如:
#include <stdio.h>
int main()
{
const int b = 3;
//char arr1[b] = {0};
char arr2[3] = { 0 };
return 0;
}
定义一个数组,[ ]里面只能是常数表达式,而里面用b时程序会报错,里面用3时可以执行,所以说b不是一个常量。
3)#define定义的标识符常量
#define 常量名 常量值
定义之后出现常量名的地方都用常量值代替。优点是在数据发生变化时,只需要在此处修改常量值即可,不用每一处修改一次。
#define定义的标识符常量可以定义在{}之外也可以定义在{}之内。
①定义常量
如:#define M 1000
②定义宏
如:#define ADD(x,y) (x+y) //(x+y)的括号可以去掉
4)枚举常量
#include <stdio.h>
enum Sex//创建一个枚举类型
{
male,//枚举类型里面的这三个元素就是枚举常量
female,
secret
};
int main()
{
enum Sex s = male;//创建的一个枚举类型的变量。
printf("%d\n",s);//如果创立枚举类型时没有给元素赋初值,那么值就是从0开始的。
return 0;
}
★注释的方法
①// 可以注释一行(常用)或多行。
②/* */ 可以注释代码块(方便)和代码行,但是不可以镶嵌使用(易错)。
③注释的作用是为了解释复杂的代码,使读的人能易于理解。
三、初识语句
1、初识选择语句
if( == )
{
}
else
{
}
如:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int input = 0;
printf("你喜欢下雨天吗?\n");
printf("喜欢扣1,不喜欢扣0\n");
scanf("%d",&input);
if (input == 1)
printf("感情细腻\n");
else
printf("大大咧咧\n");
return 0;
}
2、循环语句
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int line = 0;
while (line < 3000)
{
printf("写代码,%d\n",line);
line++;
}
if (line == 3000)
printf("完成\n");
return 0;
}
四、初识函数
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
Add(int x, int y)
{
int z = 0;
z = x + y;
return z;
}
int main()
{
int a = 0;
int b = 0;
scanf("%d%d",&a,&b);
int sum = 0;
sum = Add(a, b);
printf("%d\n",sum);
return 0;
}
用函数来计算两个数的相加。
五、初识数组
数组的定义是:一组相同类型元素的集合。
数组的命名是:数据类型 变量名[个数]
数组的访问是通过下标访问的。
如下程序是通过下标访问打印数组的全部元素:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
int i = 0;
while (i < 10)
{
printf(" %d ",arr[i]);
i++;
}
return 0;
}
六、初识操作符
1、运算操作符
加(+) 减( -) 乘(*) 除(/) 求余(%)
<1>加(+)减(-)乘(*)都与数学运算相同。
<2>除(/)与数学运算不同的是当两个操作数均为整数时为取整运算,当两个操作数有一个为小数时与数学运算相同。如:5/2,结果为2,5.0/2,结果为2.5
<3>求余(%)的两个操作数必须均为整数。
<4>运算的优先级:先乘除求余,再加减。同优先级的从左向右依次运算。
2、关系操作符
大于(>) 大于等于(>=) 小于(<) 小于等于(<=) 双等(==) 不等(!=)
<1>双等(==)是用来判断两个值是否相等,相等为真,不相等为假。
<2>不等(!=)是用来判断两个值不相等的,不相等为真,相等为假。
<3>运算的优先级:先是大于(>),大于等于(>=),小于(<),小于等于(<=),再是双等(==),不等(!=)。同优先级的从左向右依次运算。
<4>不能连等,连等与数学运算不一样,极易出错。如:5>4>3,结果为假,因为5>4为真,结果为1, 1>3为假,结果为0。
<5>字符作为操作数时,以其ASCⅡ值作为操作数。如:‘a’==97为真
<6>尽量避免小数进行双等、不等的运算,因为用小数表示时存在一定的误差。
3、逻辑操作符
与(&&) 或(||) 非(!)
<1>与(&&)、或(||)都为双目运算符,非(!)为单目运算符。
<2>当与(&&)的两个操作数均为真时,结果为真,否则为假。当或(||)的两个操作数均为假时,结果为假,否则为真。
<3>运算的优先级:先与(&&)后或(||),相同优先级从左向右运算。 非(!)的优先级最高,相同优先级从右向左运算。
<4>短路特性:当运算与()时,如果左侧为假,那右侧将不再运算。当运算或()时,如果左侧为真,那右侧将不再运算。
<5>可以用来表示一个变量的取值范围,如:-4<x<4,可表示为-4<x&&x<4。
★如果 a++&& b++&& c++这种情况,不是先将所有的单目运算符运算完再运算&&。
而是也需要考虑短路效应,得在不产生短路效应的前提下,从左往右算。
比如,如果a++为0,则b++,c++都不会执行。
4、条件操作符(唯一的三目操作符)
格式:表达式1?表达式2:表达式3
<1>首先判断表达式1的值,如果表达式 1的结果为真,则执行表达式2,表达式2的结果即为整个表达式的结果。如果表达式1的结果为假,则执行表达式3,表达式3的结果即为整个表达式的结果。
<2>可以嵌套使用,同级的执行顺序为从右向左。
5、赋值操作符
等于(=) 加等(+=) 减等(-=) 乘等(*=) 除等(/=) 求余等(%=)
<1>赋值表达式的格式:变量 赋值运算符 表达式。 赋值运算符左边必须是变量,不能是常量或表达式。
<2>加等、减等、乘等、除等、求余等,要求必须有初值。如:a+=3,意思是a=a+3,并将表达式的值赋给a。
<3>赋值运算符都属于同一优先级,同一优先级的执行顺序为从右向左。
6、逗号表达式
格式:表达式1,表达式2,......,表达式n
求解顺序为:从左向右依次计算表达式的值,最后一个表达式的值即为整个表达式的值。
7、单目操作符
①自增(++)、自减(--)
<1>自增、自减具有相同的优先级,同级执行顺序为从右向左依次执行。
<2>前置++或--:先运算后使用。如:i=2,++i,i的值为3,++i的值也为3。
后置++或--:先使用后运算。如:i=2,i++,i的值为3,i++的值为2。
<3>自增、自减都有赋值的操作,所以操作数只能是变量,不能是常量或表达式。
<4>操作的变量必须要有初值。
了解:<5>尽量不要在一个表达式中对一个变量进行多次的自增或自减,可读性差,极易出错。
<6>有时候为了避免二义性,经常使用()表明其优先结合性。
②正数(+)、负数(-)
③&取地址符
格式:& 变量名 得到变量的地址
④sizeof() 计算数据类型所占空间的大小。
<1>sizeof()计算的结果是size_t类型的
<2>size_t是无符号整型的
<3>对size_t类型的数据进行打印,可以使用%zd(如果%zd不行,可以使用%u或%d)
<4>sizeof后面的括号在括号中写的不是类型的时候,括号可以省略(因此可以证明sizeof不是函数)
⑤~按位取反
二进制位按位取反。如:int a=2 ,int b=~a
a:0000 0010 2
~a:1111 1101 -3
⑥ * 间接访问操作符(解引用操作符)
⑦(类型)强制类型转换
如: int a=3.14 强制类型转换 int a=(int)3.14
8、移位操作符
知识铺垫:
整数的二进制表示形式有3种:原码、反码、补码
原码:按照数值的正负,直接写出的二进制序列就是原码。
反码:原码的符号位不变,其他位按位取反。
补码:反码的二进制+1就得到补码。
原码---->反码----->补码
取反 +1
补码---------------->原码
取反、+1
(以上转化可自行验证,结果一定是正确的)
对于正整数来说,原码、反码、补码均相同。
对于负整数来说,反码、补码都是需要计算的。
整数在内存中存储的都是补码的二进制序列。
整数在计算机中的运算都是以补码进行运算的。
1个字节是8个bit位,一般来说一个整数是4个字节,也就是32个bit位。
对于有符号的整数来说,最高位为符号位,其余位为有效位。
符号位是1表示负数。
符号位是0表示正数。
对于无符号的整数来说,没有符号位,所有位均为有效位。
1、左移(<<)
一个数的二进制位向左移动,相当于 这个数*2的移动位数次方
移动规则:左移丢弃,右边补0(正数负数都一样)
例1:int m=10,int n=m<<1;
未移动前的m:00000000 00000000 00000000 00001010
m移动后的n: 00000000 00000000 00000000 00010100
移动后,m=10,n=20; 原本的数不会变!!!
例2:int m=-7,int n=m<<1
-7的原码: 10000000 00000000 00000000 00000111
-7的反码: 11111111 11111111 11111111 11111000
-7的补码: 11111111 11111111 11111111 11111001
未前移动前的n:11111111 11111111 11111111 11111001
m移动后的n: 11111111 11111111 11111111 11110010
n的反码: 11111111 11111111 11111111 11110001
n的原码: 10000000 00000000 00000000 00001110
移动后,m=-7,n=-14;
2、右移(>>)
①逻辑右移
一个数的二进制位向右移动,一般移动之后数变成正数。
移动规则:右边直接丢弃,左边补0
②算数右移
一个数的二进制位向右移动,相当于这个数/2的移动位数次方
移动规则:右边直接丢弃,左边补原符号
★对于移位运算符,不要移动负数位,这个是标准未定义的。
9、位操作符
按位与(&) 按位或(|) 按位异或(^)
<1>按位与,当两个二进制数相同位置的数都为1时,结果的二进制数此位置为1。如:
0000 1101
0000 1010
结果为0000 1000
应用:一个数&1,可以知道这个数最低位为1还是0。可以配合移位操作符,知道这个数每一位的0或1,或者知道这个数有多少位0或多少位1
<方法一>
#include<stdio.h>
int Number_of_bits(int n)
{
int sum = 0;
for (int i = 0; i < sizeof(n)*8; i++)
{
sum += (n >> i) & 1;
}
return sum;
}
int main()
{
int a = 0;
scanf("%d",&a);
int ret=Number_of_bits(a);
printf("一个整型中,%d的二进制表示中有%d个1\n",a,ret);
return 0;
}
<方法二>
#include<stdio.h>
int Number_of_bits(int n)
{
int count = 0;
while (n)
{
if (n % 2 == 1)
count++;
n /= 2;
}
return count;
}
int main()
{
int a = 0;
scanf("%d", &a);
int ret = Number_of_bits(a);
printf("一个整型中,%d的二进制表示中有%d个1\n", a, ret);
return 0;
}
<方法三>
#include<stdio.h>
int Number_of_bits(int n)
{
int count = 0;
while (n)
{
n = n & (n - 1);
count++;
}
return count;
}
int main()
{
int a = 0;
scanf("%d", &a);
int ret = Number_of_bits(a);
printf("一个整型中,%d的二进制表示中有%d个1\n", a, ret);
return 0;
}
<2>按位或,当两个二进制数相同位置的数有至少一个为1时,结果的二进制数此位置为1。如:
0000 1101
0000 1010
结果为0000 1111
<3>按位异或,当两个二进制数相同位置有且仅有一个1时,结果的二进制数此位置为1。如:
0000 1101
0000 1010
结果为0000 0111
应用及结论:
①a^a=0;
②0^a=a;
③不创建临时变量,交换两个数的内容。
<方法一>利用算数运算符实现(弊端是a+b的和超过整型范围,那么就会出问题)
#include<stdio.h>int main()
{
int a = 0;
int b = 0;
scanf("%d%d",&a,&b);
printf("交换前a=%d,b=%d\n",a,b);
a = a + b;
b = a - b;
a = a - b;
printf("交换后a=%d,b=%d\n",a,b);
return 0;
}
<方法二>
#include<stdio.h>
int main()
{
int a = 0;
int b = 0;
scanf("%d%d",&a,&b);
printf("交换前a=%d,b=%d\n",a,b);
a = a ^ b;
b = a ^ b;
a = a ^ b;
printf("交换后a=%d,b=%d\n",a,b);
return 0;
}
10、下标引用、函数调用和结构成员操作符
[ ] () . →
1、下标引用操作符( [ ])
①有两个操作数,一个数组名、一个索引值。
如:
int arr[]={1,2,3,4,5};
printf("%d\n",arr[2]); //下标引用操作符[]的两个操作数是arr和2
2、函数调用操作符(())
①操作数由两部分组成:函数名、函数参数。最少有一个操作数
3、成员访问操作符(.)(->)
①语法格式: 结构体变量.成员变量
结构体指针变量->成员变量
11、表达式求值
1、隐式类型转换
①整型提升
<1>整型提升:表达式中的字符和短整型操作数在使用之前被转化为普通整型(表达式中各种长度小于int长度的,都必须先转化为int或unsigned int类型),这种转换称为整型提升。
如:char a,b,c;
a=b+c;
b和c的值被提升为普通整型,再参加加法运算,加法运算完成后,结果将被截断,再存储于a中。
int main()
{
char a = 3;
char b = 127;
char c = a + b;
printf("%d\n",c);
return 0;
}
☆ %u打印的是无符号整型
<2>整型提升规则
整型提升是根据变量的数据类型的符号位来提升的
a.有符号整数的整型提升,高位补原符号位
b.无符号整数的整型提升,高位补0
char c=1;
sizeof(c); //1
sizeof(+c); //4
sizeof中的c不参与运算,但是看的是,如果参加运算,之后的类型。
②算数转换
当一个操作符的操作数类型不一样时,只有当一个操作数的类型向另一个操作数的类型转换之后才可以进行后续操作。
long double
double
float
unsigned long int
long int
unsigned int
int
上面的表格是,由下面的类型转换成上面的类型,原则就是向更高精度的类型转换。
③问题代码:
<1>a*b+c*d+e*f
<2>c + --c
12、操作符的属性
复杂表达式的求值有三个影响因素。
1、操作符的优先级
2、操作符的结合性
3、是否控制求值顺序
七、初识常见关键字
预处理指令define include不是关键字
常见关键字: 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
①typedef 类型重命名
如:
#include <stdio.h>
int main()
{
unsigned int num = 100;//类型名太长了,可以重新用typedef进行类型重命名
return 0;
}
#include <stdio.h>
typedef unsigned int u_int;
int main()
{
u_int num = 100;
return 0;
}
②static 静态的
<1>static修饰局部变量——静态局部变量
#include <stdio.h>
void test()
{
int a = 1;
a++;
printf("%d\n",a);
}
int main()
{
int i = 0;
while (i < 10)
{
test();
i++;
}
return 0;
}
#include <stdio.h>
void test()
{
static int a = 1;
a++;
printf("%d\n",a);
}
int main()
{
int i = 0;
while (i < 10)
{
test();
i++;
}
return 0;
}
出作用域不销毁,重新进入作用域不创建。
主观上就是生命周期变长了,作用域并没有发生改变。
☆本质上,普通的局部变量放在栈区,而被static修饰的局部变量放在静态区。
<2>static修饰全局变量——静态局部变量
static修饰的全局变量只能在本源文件内使用,在另外的源文件中用extern声明之后仍无法使用,static使全局变量的外部链接属性变为了内部链接属性。
<3>static修饰函数——静态函数
static修饰的函数只能在本源文件内使用,在另外的源文件中用extern声明之后仍无法使用,static使函数的外部链接属性变为了内部链接属性。
★全局变量是具有外部链接属性的,这种属性决定了全局变量在多个文件之间可以相互使用的。
★栈区
局部变量、函数的形式参数、临时属性的变量
堆区
用来动态内存开辟的
静态区
静态变量、全局变量
★extern声明来自外部文件的符号
①声明一个变量 extern int 变量名; //不能对变量进行赋值,赋值会报出重定义的错误。
②声明一个函数 extern 返回值类型 函数名(参数列表); //参数列表中只写类型就可以,也可以写上变量名,变量名也可以与定义时变量名不相同。
八、#define定义常量和宏
①define定义常量:#define MAX 10000
②define定义宏:#define Add(X,Y) X+Y
define定义宏是参数的替换,Add(X,Y)替换成X+Y
九、初识指针
①电脑有32位的和64位的,如32位的,是由32根地址线通过通电与不通电来给内存单元标序号的,32位的有32位二进制数,每一个二进制数都是一个内存单元的地址,每一个内存单元是一个字节。
②一个变量占有几个字节,但是用取地址符号得到其地址时取出的是第一个字节的地址。
③指针变量:
#include <stdio.h>
int main()
{
int a=0;//变量的创建
int * pa=&a;//指针变量存储的是地址。
*pa=20;//改变了a的值,*是解引用操作符。
return 0;
}
④指针变量的大小:32位的是4个字节,64位的是8个字节。因为用来表示地址的二进制数只占四或八个字节。
★我们一般说的指针是指针变量。
★指针变量里面放的东西一般都默认为是一个地址。
十、初识结构体
struct(名字)
如:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
struct Stu
{
char name[20];
int age;
double score;
};
int main()
{
struct Stu s = { "阿大", 20, 85.5 };
printf("%s %d %lf\n",s.name,s. age,s.score);
struct Stu* ps = &s;
printf("%s %d %lf\n",(*ps).name,(*ps).age,(*ps).score);
printf("%s %d %lf\n", ps->name, ps->age, ps->score);
return 0;
}
★细碎知识点
①scanf函数的返回值,输入了几个数据,那么scanf的返回值就是多少。
②EOF,EOF的值是-1,如果函数读取失败,那么就是EOF。
③多组输入时,常使用EOF来当作循环条件的一部分。
④ctrl z可以结束程序。
⑤辨析:
数字 0 ASCⅡ 是0
字符'0' ASCⅡ 是48
转义字符 '\0' ASCⅡ是0