二、常量、变量和数据类型
-
关键字(Keyword)
又称为保留字,C语言中预先预定的具有固定含义的一些单词(if,while,...) 由ANSI标准定义的C语言关键字共32个。
-
标识符(Identifier)
系统预定义标识符
用户自定义标识符
-
运算符(Operator)
34种(+,-,*,...)
-
分隔符(Separator)
空格、回车/换行、逗号等
-
其他符号
大花括号“{”和“}”通常用于标识函数体或者一个语句块“/”和“/”是程序注释所需的定界符
b,B,KB,MB,GB,TB
-
一个字节有点多大?
-
可以表示数字0~255
-
保存一个字符(英文字母、数字、符号),ASCII编码
-
两个字节保存一个汉字
-
GB2312,6763字
-
GB13000.120920字
-
GB18030,27533字
-
BIG5,13000字
-
-
两个字节保存一个宽字符,UNICODE编码
-
65536个字符
-
-
1.大小
-
bit,中文叫法:位
-
Byte,中文叫法:字节
-
Kilobyte(KB),中文叫法:K
-
Megabyte(MB),中文叫法:兆
-
Gigabite(GB),中文叫法:G
-
Terabyte(TB),中文叫法:T
-
GB2312,6763字
-
GB13000.120920字
-
GB18030,27533字
-
BIG5,13000字
-
1TB==1024GB 1GB==1024MB 1MB==1024KB 1KB==1024B 1B==8b
2.数据类型
程序体现:
#include<stdio.h>
int main()
{
printf("%lu\n",sizeof(char)); //1 0~2 8 -1
printf("%lu\n",sizeof(short)); //2 0~2 16 -1
printf("%lu\n",sizeof(int)); //4 0~2 32 -1
printf("%lu\n",sizeof(long)); //4
printf("%lu\n",sizeof(long long)); //8 0~2 64 -1
return 0;
}
浮点数的精度和范围
1)浮点数的陷阱
#include<stdio.h>
int main()
{
float f;
f = 123.456;
if(f == 123.456)
{
printf("f确实等于123.456");
}else
{
printf("实际上,f等于%f\n",f);
}
return 0;
}
//实际上,f等于%f\n 123.456001
3.ASCII码表
1)正确的输入得到正确的结果
#include <stdio.h>
#include <stdlib.h>
int my_atoi(char *str)
{
int total = 0;
while (*str == ' ')
{
str++;
}
if (*str == '-')
{
flag = -1;
}else if(*str == '+')
{
str++;
}
//迭代
while (*str >= '0' && *str <= '9')
{
total = total * 10 + (*str - '0');
str++;
}
return total;
}
int main()
{
char str[5] = " -12345"; //" --12312qad123" 程序的局限
int total = 0;
//total = (str[0] - '0') * 100 + (str [1] - '0') * 10 + (str[2] - '0') * 1;
total = my_ayoi(str);
printf("%d\n",total);
return 0;
}
2)小写字母转换为大写字母
#include<stdio.h>
int main()
{
char c = 'a'
c = c - ('a' - 'A');
printf("%c\n", c);
return 0;
}
#include<stdio.h>
void upper(char *str != '\0')
{
if(*str <= 'z' && *str >= 'a')
{
*str = *str - ('a' - 'A')
}
str++;
}
int main()
{
//char *str = "Helloworld, I love China!"; / 存于ROdata段,不可修改。段错误
//数组 str[]在栈空间可修改 / " "ROdata段 " "需要多少[]取多少
char str[] = "Helloworld, I love China!";
return 0;
}
const关键字
-
用const修饰定义的变量为常量 ? const int i = 0;
-
const变量只能在定义时赋值,然后不能在改变其值。
4.类型转换
两个栓数类型的操作数做算数运算,比如a+b,如果两边操作数的类型不同,编译器会自动做类型转换,是两边类型相同之后才做运算。
如果又一遍的类型是 long double,则把另一边也转换成long double。
否则,如果有一边的类型是double,则把另一边也转成double。
否则,如果有一边的类型是float,则把另一边也转成float。
否则,两边应该是整形,对a和b做Intger Promotion。
-
混合运算
-
当操作数1和操作数2的类型不同时,即为混合运算。
-
当运算时要求类型应相同。即先转换,在运算,
-
1)赋值类型转换
#include <stdio,h>
int main()
{
char a;
unsigned int b;
a = 255;
b = a;
printf("%d\n",a); //- 1
printf("%u\n",b); // -1
return 0;
}
无符号数和有符号数不能混着用。
若果赋值或初始化时等号两边的类型不同,则编译器会把等号右边的类型转换成等号左边的类型在做赋值。
2)强制类型转换
#include <stdio.h>
int main()
{
printf("%lf\n",(double)(3/4));
return 0;
}
-
强制类型转换给C语言编程带来了极大地灵活性,也正是这种灵活性,也埋下了无数的隐患,
-
当目的结构的空间大于源结构的空间时,要重点关注内存访问超过源结构范围的情形,可能越界。
-
当目的结构空间小于源结构的空间时,要重点关注对目的的结构赋值不能完全覆盖源结构范围的情形,可能遗漏。
-
与结构体之间的强制类型转化相比,基本数据结构的强制类型转换更同意出现上面描述的情况,使用的时候,一定要慎之又慎。
5.变量
-
字母开头,后跟字母,数字和下划线组成。
-
C语言中的关键字不能作为变量名
-
注意:变脸必须先定义或声明后使用;
-
变量中存放的只能是数据,而且只能是一个数据,往变量中存放数据的操作称为赋值。
-
#include <stdio.h>
int main()
{
int a = 100;
}
1.定义和声明
声明可以声明很多次,定义只能定义一次。定义为变量分配地址和存储空间。
注意:
- 变量必须先定义或声明后使用; - 变量中存放的只能是数据,而且只能是一个数据,往变量中存放数据的操作称为赋值。 - 变量的“名”和变量的“值”不同,变量的“名”是该变量所代表的存储单元的标志,而变量的“值”是指存储单元中的内容。
//1.c
#include <stdio,h>
//声明
extern int g_a;
int main()
{
extern int g_a;
int total = 100;
printf("%d\n",g_a);
return 0;
}
声明一般放.h文件,放.c文件可能会重定义
//2.c
int g_a = 250;
//int g_a = 250;
//重定义
//gcc 1.c 2.c -o main
//链接 gcc 1.0 2.0
//预编译 编译 汇编 + 链接
2.初始化和赋值
#include <stdio,h>
extern int g_a;
int main()
{
//初始化,不能再赋值
const int total = 0;
//错误
int total = 100;
printf("%d\n",g_a);
return 0;
}
#include <stdio.h>
int main()
{
int a = 100;
int b = 200;
int total = 0;
total = a + b;
printf("%d\n",total);
return 0;
}
3.变量的作用域
代码块作用域
全局作用域
文件作用域
原型作用域
函数作用域
-
重定义
#include<stdio.h>
tin main()
{
int a = 100;
int b = 200;
int a = 300;
int b = 400;
return 0;
}
-
变量隐藏
#include<stdio.h>
tin main()
{ //1 3开始
int a = 100;
int b = 200;
{
//1结束
printf("1:%d %d\n",a,b);
//2开始
int a = 300; //隐藏
int b = 400;
printf("2:%d %d\n",a,b);
} //2结束
printf("3:%d %d\n",a,b);
return 0;
} //3结束
1)全局变量
全局变量定义在所有的函数体之外,它们在程序开始运行时分配存储空间,再程序结束时释放存储空间,再任何函数中都可以访问全局变量。
#include <stdio.h>
static int g_total = 0;
//加static,在本文件中可见
void fun()
{
g_total++;
}
int main()
{
printf("%d\n",g_total); //0
fun();
printf("%d\n",g_total); //1
return 0;
}
/*
静态全局变量内部链接属性,在别的.c文件不可用
函数前也可以写
*/
2)局部变量
函数中定义的变量称为局部变量
局部变量的含义:
1.一个函数中定义的变量不能被另一个函数调用
2.每次调用函数时局部变量都表示不同的存储空间
#include <stdio.h>
void foo (void)
{
int i;
print("%d\n",i);
i = 777;
}
int main(void)
{
foo();
print("hello\n");
foo();
return 0;
}
局部变量可以用类型相符的任意表达式来初始化,而全局变量只能用常量表达式(Constant Expression)初始化。
-
链接属性
-
external(外部)
-
internal(内部)
-
none(无)
static与extern
加static是把外部链接属性改成内部连接属性
如果具有外部链接属性加extern
-
4.存储类型
-
变量的存储类型是指存储变量的内存类型
-
主要包括 自动变量(auto) 静态变量(static) 寄存器变量(register)
#include <stdio.h>
#include <stdlib.h>
int countbit(unsigned int x)
{
int total = 0;
unsigned int mask = 1u;
while (x > 0)
{
total += x & mask;
x >>= 1;
}
return total;
}
void print_bin(unsigned int x)
{
int i;
unsigned int mask = 1u;
for (i = sizeof(int)* 8 - 1; i >= 0; i--)
{
if ((mask << i) & x)
{
putchar('1');
}else
{
putchar('0');
}
if (i % 4 == 0)
{
putchar(' ');
}
}
putchar('\n');
}
unsigned int ror(unsigned int x, int n)
{
int i;
while (n < 0)
{
n = n + 32;
}
n = n % 32;
for (i = 0; i < n; i++)
{
x = (x >> 1) + (x << 31);
}
return x;
}
unsigned int ror2(unsigned int x, int n)
{
while (n < 0)
{
n = n + 32;
}
n = n % 32;
return (x >> n) + (x << (32 - n));
}
int main()
{
unsigned int x = 0xdeadbeef;//0xefdeadbe;
print_bin(x);
print_bin(ror(x, -18));
print_bin(ror2(x, -18));
return 0;
}
结构体存储
#include <stdio.h>
typedef struct date
{
int year;
int month;
int day;
}date;
typedef struct student
{
int ID;
char name[20];
char sex;
int age;
date birthday;
}student;
void print_student(student *s)
{
printf("%d %s %c %d ", s->ID, s->name, s->sex, s->age);
printf(":%d-%d-%d\n", s->birthday.year, s->birthday.month,s->birthday.day);
}
int main()
{
student s1 = {1,"Qizhen",'m',18, {2006, 1, 21}};
student s2 = {2,"JiJiaxin",'f',17, {2007, 5, 20}};
print_student(&s1);
print_student(&s2);
return 0;
}
5.变量的特性:
-
变量的名
-
变量的值
-
变量的地址
-
变量的类型
-
变量的存储类型
-
变量的作用域
-
变量的生命周期
6.排序方式
1). 插入排序
-
直接插入排序:将数组中的所有元素依次跟前面已经排好的元素相比较,如果选择的元素比已排序的元素小,则交换,直到全部元素都比较过。
-
希尔排序:也称递减增量排序算法,是插入排序的一种更高效的改进版本。通过选定一个增量序列,将待排序记录序列分割成为若干子序列分别进行直接插入排序,待整个序列中的记录“基本有序”时,再对全体记录进行依次直接插入排序。
2). 选择排序
-
简单选择排序:从未排序的数组中选择最小的元素,将其放在已排序的数组的末尾。重复这个过程,直到所有元素都排序完毕。
-
堆排序:利用堆这种数据结构所设计的一种排序算法。通过构建大顶堆(或小顶堆),不断取出堆顶元素(最大或最小),然后重新调整堆,直到所有元素都被取出。
3). 交换排序
-
冒泡排序:重复地遍历要排序的数列,一次比较两个元素,如果它们的顺序错误就把它们交换过来。遍历数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。
-
快速排序:通过选取一个“基准”元素,将数组分为两个子数组,一个包含所有小于基准的元素,另一个包含所有大于基准的元素。然后递归地对这两个子数组进行快速排序。
4). 归并排序
-
归并排序:采用分治法的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。
5). 基数排序
-
基数排序:按照低位先排序,然后收集;再按照高位排序,然后再收集;以此类推,直到最高位。基数排序是非比较型整数排序算法,其原理是将整数按位数切割成不同的数字,然后按每个位数分别比较。
(1)冒泡排序
#include <stdio.h>
void print_array(int *v,int len)
{
int i;
for(i = 0;i < len; i++)
{
printf("%d",v[i]);
printf("\n");
}
}
void bubble_sort(int *v, int len)
{
int i;
int j;
int tmp;
for(i = 0;i < len - 1; i++)
{
for(j = 0; j < len - 1 - i;j++)
{
if (v[j+1] < v[j])
{
tmp = v[j];
v[j] = v[j + 1];
v[j + 1] = tmp;
}
}
}
}
int main()
{
int a[10] = {-1, -12, 13, 41, 15, 63, -7, 89, 91, -10};
//一个个比较,交换;
print_array(a, 10);
bubble_sort(a, 10);
print_array(a, 10);
return 0;
}
(2)快速排序
#include <stdio.h>
// 函数声明
void quickSort(int arr[], int low, int high);
int partition(int arr[], int low, int high);
int main() {
int arr[] = {10, 7, 8, 9, 1, 5};
int n = sizeof(arr) / sizeof(arr[0]);
quickSort(arr, 0, n-1);
printf("Sorted array: \n");
for (int i = 0; i < n; i++)
printf("%d ", arr[i]);
printf("\n");
return 0;
}
// 快速排序函数
void quickSort(int arr[], int low, int high) {
if (low < high) {
// pi 是分区后的索引,arr[pi] 现在在正确的位置
int pi = partition(arr, low, high);
// 递归地对左半部分进行快速排序
quickSort(arr, low, pi - 1);
// 递归地对右半部分进行快速排序
quickSort(arr, pi + 1, high);
}
}
// 分区函数
int partition(int arr[], int low, int high) {
int pivot = arr[low]; // 选择第一个元素作为基准
int i = low - 1; // 小于基准的元素的索引
for (int j = low; j <= high - 1; j++) {
// 如果当前元素小于或等于pivot
if (arr[j] <= pivot) {
i++; // 增大小于pivot的元素的索引
// 交换arr[i]和arr[j]
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
// 交换arr[i+1]和arr[high](或pivot)
int temp = arr[i + 1];
arr[i + 1] = arr[high];
arr[high] = temp;
return (i + 1); // 返回基准的正确位置
}
(3)选择排序
#include <stdio.h>
// 函数声明
void selectionSort(int arr[], int n);
// 选择排序函数
void selectionSort(int arr[], int n)
{
int i, j, minIndex, temp;
// 移动未排序数组的边界
for (i = 0; i < n-1; i++)
{
// 寻找最小元素的索引
minIndex = i;
for (j = i+1; j < n; j++)
{
if (arr[j] < arr[minIndex])
{
minIndex = j;
}
}
// 将找到的最小元素与未排序数组的第一个元素交换
temp = arr[minIndex];
arr[minIndex] = arr[i];
arr[i] = temp;
}
}
int main()
{
int arr[] = {64, 25, 12, 22, 11};
int n = sizeof(arr)/sizeof(arr[0]);
printf("原始数组: \n");
for (int i = 0; i < n; i++)
printf("%d ", arr[i]);
printf("\n");
selectionSort(arr, n);
printf("排序后的数组: \n");
for (int i = 0; i < n; i++)
printf("%d ", arr[i]);
printf("\n");
return 0;
}