强制转换
int a = 5;
float b = a / 2; // 2.000000
float b = (float)a / 2; // 2.500000
#include <stdio.h>
int main(int argc, char const *argv[])
{
int a = 5;
float b = (float)a / 2;
printf("%d %f\n", a, b);
return 0;
}
分支语句
if
基本结构:
if(表达式)
{
// 你符合这个条件的时候,就执行语句块1
语句块1;
}
else
{
// 你不符合这个条件的时候,就执行语句块2
语句块2;
}
分层结构:
if(表达式1)
{
语句块1;
}
else if(表达式2)
{
语句块2;
}
else
{
// 都不符合条件
语句块3;
}
圆角矩形-起止 菱形-判断 矩形-处理 平行四边形-输入输出
#include <stdio.h>
int main(int argc, char const *argv[])
{
int a, b;
scanf("%d %d", &a, &b);
if(a > b)
{
printf("a > b\n");
}
else if(a == b)
{
printf("我们都一样\n");
}
else
{
printf("a < b\n");
}
return 0;
}
嵌套结构:
if(表达式1)
{
if(表达式2)
{
语句块1.1;
}
else
{
语句块1.2;
}
}
else
{
语句块2;
}
总结:
1) if后面可以没有else,但是 else 前面去须有if
2) if和else后面的{}可以省略,但是只能匹配后面一条语句
int a;
scanf("%d", &a);
if(a > 10)
a++;
printf("%d\n", a);
else
printf("on Ok\n");
// 会报错,省略大括号之后,符合条件的话只执行 a++ 这一条语句
练习:
实现大小写转换(用getchar、putchar实现)
输入大写字母输出小写字母,
输入小写字母输出大写字母,
输入的不是字母,提示输入错误。
##
#
switch_case
基本结构:
switch(变量或表达式)
{
case 常量1: 语句块1; break;
case 常量2: 语句块2; break;
case 常量3: 语句块3; break;
...
case 常量n: 语句块n; break;
// 如果以上常量都不符合的情况下
// 就执行 default 后面的语句块
default: 语句块 n+1;
}
执行顺序:
判断switch后面的表达式的结果,和case后的常量相匹配,如果匹配成功,就执行对应的语句块,如果没有匹配成功就执行default后面的语句块,遇到break结束
#include <stdio.h>
int main(int argc, char const *argv[])
{
int a;
scanf("%d", &a);
switch (a)
{
case 1: printf("a = %d\n", a); break;
case 2: printf("a = %d\n", a); break;
case 3: printf("a = %d\n", a); break;
default: printf("other\n"); break;
}
return 0;
}
注意:
1. 表达式不能是浮点型或字符串
2. case后面的break可以省略,但是省略时会顺序执行,直到遇到break结束;
练习:
从终端输入一个学生的成绩,判断学生成绩,打印成绩级别
【90 - 100】 A
【80 - 89】 B
【70 - 79】 C
【60 - 69】 D
< 60 sorry you lost
从终端输入3个整数;从大到小排序
#
#
循环语句
for循环
1. 基本结构
// for循环后面不要使用 分号
for(表达式1; 表达式2; 表达式3)
{
语句块;
}
表达式1:赋初值
表达式2:循环的终止条件
表达式3:增值减值语句
执行顺序:
首先执行表达式1进行赋值,然后判断表达式2是否成立,如果成立就进入循环执行语句块
再执行表达式3进行增值或减值语句然后继续判断表达式2是否成立,直到表达式2不成立退出循环
#include <stdio.h>
int main(int argc, char const *argv[])
{
for (int i = 1; i <= 10; i++)
{
printf("您已重生%d次\n", i);
}
return 0;
}
2. 嵌套结构
for(表达式1; 表达式2; 表达式3)
{
for(表达式4; 表达式5; 表达式6)
{
语句块7;
}
}
执行顺序:
首先执行表达式1,判断它符不符合表达式2的条件。
符合进入内部的循环,执行表达式4,然后判断表达式5是否成立,
执行语句块,表达式6执行占增值或减值语句,
继续判断表达式5是否成立,直到表达式5不成立了,
来到表达式3,执行增值或减值语句,
继续判断表达式2是否成立,直到表达式2不成立,循环结束
执行顺序:124576(直到5不成立) 324576(直到表达式2不成立) 循环结束
int i, j;
for (i = 1; i <= 10; i++)
{
for (j = 1; j <= 10; j++)
{
printf("%d.%d, ", i, j);
}
printf("\n");
}
基本结构的变形
变形一:
int i = 0;
for(;表达式2; 表达式3)
{
语句块;
}
变形二:
int i = 0;
for(; 表达式2; )
{
语句块;
表达式3
}
变形三
int i = 0;
for(; ;) // 死循环
{
if(表达式2)
{
语句块;
表达式3;
}
else
{
break;
}
}
练习:
打印以下图案:要求行数从终端输入。
输入:5
解题思路:用for循环嵌套实现外层循环控制行数,内层循环控制每行个数。
// 结果
*
**
***
****
*****
#
#
while循环
定义循环变量并赋值;
while(判断条件)
{
语句块;
增值或减值语句;
}
执行顺序:
首先定义循环变量并赋值,然后判断是否符合终止条件,
如果符合就进入循环执行语句块及增值减值语句,
然后继续判断,直到不成立退出循环
int i = 1, sum = 0;
while (i <= 10) // 1-10的累加和
{
sum = sum + i;
i++;
}
printf("%d\n", sum);
练习:一起来找错
#include
int main()
{
while(1>0);
print("hello");
return 0;
}
do_while循环
定义循环变量并赋值
do
{
语句块;
自增自减语句;
}while(终止条件)
先执行后判断
练习:实现 1-10的累加和
int i = 1, sum = 0;
do
{
sum += i;
i++;
}while(i <= 10)
printf("%d\n", sum); // 55
死循环
for(;;){};
while(1){};
while(1); // 让程序卡死在这里等待
循环控制语句
break; continue;
break:直接结束循环
continue:结束本次循环,继续下一次循环
使用场景:
使用在循环语句中,结束循环
使用时需要有判断条件
for (int i = 0; i < 5; i++)
{
if(i == 3)
{
// break;
continue;
}
printf("%d\n", i);
}
练习:
从终端上输入一个字符并在终端上输出这个字符,当输入字符为‘q’时,程序结束。
#
练习:
1.循环输入一个5位数,判断它是不是回文数。当输入0时循环结束。
即12321是回文数,个位与万位相同,十位与千位相同。
2. 写程序实现功能:读入两个整数(data1和data2)和一个运算符(op),计算表达式data1 op data2 的值.其中op可以为+、-、*、/四个符号中的任一种(用switch语句实现)
3. 输入任意两个数,输出两数之间(包括这两个数)偶数之和。
思路:将输入的两个数a,b中小的数a,依次加1,加到b的值,每次循环判断这个数a是否为偶数,是则累加到sum中
4. 打印99乘法表
数组
1. 概念:
具有一定顺序的若干变量的集合
2. 定义格式:
存储类型 数据类型 数组名[元素的个数]
int arr[5];
数组名:代表数组首元素的首地址,arr是地址常量,不能为左值,不能被赋值
3. 访问元素:
数组名[下标]:下标从0开始
访问第一个元素:arr[0];
访问第n个元素:arr[n-1];
注意数组越界问题
4. 特点:
1) 数组的元素数据类型相同
2) 内存连续
5. 注意:
1) 数组的数据类型就是数组元素的数据类型
2) 数组名要符合标识符命名规则
3) 在同一个函数中,数组名不要与变量名相同
4) 下标重0开始,到n-1 结束
6. 分类:
一维数组、二维数组
一维数组
只有一个下标的数组
1. 格式:
存储类型 数据类型 数组名[元素个数];
2. 访问元素:
数组名[下标],下标是从0开始
3. 数组名:
数组的首地址
4. 初始化:
4.1. 全部初始化:
int arr[5] = {1, 2, 3, 4, 5};
printf("%d\n", arr[0]); // 1
printf("%d\n", arr[2]); // 3
printf("%d\n", arr[4]); // 5
4.2. 部分初始化:
int arr[5] = {1, 2}; // 1 2 0 0 0 初始化的部分照常赋值,未初始化部分值为0
未初始化的部分为0
4.3. 未初始化:
随机数,需要单个元素赋值
int arr[5]; // 这样就是随机值
int arr[5]; // 我们只能进行单个赋值
arr[0] = 1;
arr[1] = 2;
arr[2] = 3;
5. 定义空数组
5.1. 全部初始化:
int arr[5] = {0, 0, 0, 0, 0};
5.2. 部分初始化:
int arr[5] = {0};
5.3. 空数组:
int arr[5] = {}; 所有元素都会被初始化为 '\0'
(即空字符)
6. 引用
6.1. 先定义后引用
6.2. 每次只能引用一个数组元素 arr[i],如果想引用所有元素可以循环遍历
int arr[5] = {1, 2, 3, 4, 5};
for (int i = 0; i < 5; i++){
printf("%d\n", arr[i]);
}
6.3. 引用时防止数组越界,虽然有时编译器不会报错
printf("%p\n", &arr[i]);
6.4. 打印数组元素的地址用 %p 格式
0xbfe86b08
0xbfe86b0c
0xbfe86b10
0xbfe86b14
0xbfe86b18
int
1 | 2 | 3 | 4 | 5 | 6 |
18 1c 20 24 28 32
7. 内存分配:
一一对应
8. 数组遍历
把循环变量作为数组的下标,用 for 循环遍历
int arr[5] = {};
int i; // 循环变量
for(i = 0; i < 5; i++)
{
scanf("%d", &arr[i]);
}
for(i = 0; i < 5; i++)
{
printf("%d", arr[i]);
}
printf("\n");
printf("%d\n", arr[5]); // 数组越界,错误
9. 数组大小:
1. 数组元素个数 * 数据类型大小
2. sizeof(数组名)
int arr[5]; // 20
double b[2]; // 16
char c[32]; // 32
printf("%d\n", sizeof(arr)); // 20 字节
10. 计算元素的个数:
1. sizeof(数组名) / sizeof(数据类型)
2. 直接观察
练习:
计算斐波那契数列前15项并逆序输出
1 1 2 3 5 8 13 21 .....
#
#
int a[5] = {1, 2, 3, 4, 5};
for(int i = 0; i < 5; i++)
printf("%d\n", a[i]);
a[5] = {0}; // 越界了
int a[5] = {0}; // 这样也不行
a[0] = 0; // 这样可以,就是太麻烦了
清零函数
1. bzero
#include <strings.h>
void bzero(void *s, size_t n);
功能:将空间设置为0
参数:
s:要清空的空间的首地址
n:字节大小
返回值:无
#include <stdio.h>
#include <strings.h>
int main(int argc, char const *argv[])
{
int arr[5] = {1, 2, 3, 4, 5};
bzero(arr, sizeof(arr));
for (int i = 0; i < 5; i++)
{
printf("%d\n", arr[i]);
}
return 0;
}
2. memset
#include <string.h>
void *memset(void *s, int c, size_t n);
功能:将内存空间设置为0
参数:
s:要清空的空间的首地址
c:要设置的值,设置为0
n:字节大小
返回值:要清空的空间的首地址
#include <stdio.h>
#include <string.h>
int main(int argc, char const *argv[])
{
int arr[5] = {1, 2, 3, 4, 5};
memset(arr, 0, sizeof(arr));
for (int i = 0; i < 5; i++)
{
printf("%d\n", arr[i]);
}
return 0;
}
字符数组
字符数组可以存放字符串
1. 概念:
元素的数据类型为字符型的数组
2. 形式:
char a[] = {'a', 'b', 'c'}; // 逐个字符的赋值,sizeof(a) == 3
char b[] = {"hello"}; // 用字符串赋值,sizeof(b) == 6
// 因为字符串后面会默认补一个\0,所以是6
char c[] = "hello"; // 用字符串赋值,sizeof(b) == 6
char str[32] = "hello"; // szieof(str) == 32
char d[]; // 这样是不可以的,会报一个没有数组大小
char d[] = {}; // 大小是0,随便放一个东西就会越界
注意:字符串赋值常常省略数组的长度,需要注意数组越界问题
3. 字符数组输入输出
输入
char s[32] = {};
1) scanf("%s", s);
#include <stdio.h>
int main(int argc, char const *argv[])
{
char s[32]={};
scanf("%s",s);
printf("%s\n",s);
return 0;
}
hq@Ubuntu:~/song$ ./a.out
hello
hello
*输入字符串不能含有空格*,因为scanf输入字符串遇到空格或者 \n 都会认为字符串输入结束,空格后面就不能再存放到数组里面
如果需要输入空格就按以下格式输入
#include <stdio.h>
int main(int argc, char const *argv[])
{
char s[32]={};
scanf("%[^\n]",s);
printf("%s\n",s);
return 0;
}
hq@Ubuntu:~/song$ ./a.out
hello world
hello world
%s
读取的是单个单词,遇到空白字符(如空格、制表符、换行符等)会停止,而%[^\n]
读取的是整行内容,遇到换行符才停止。
2) 循环遍历
for(int i = 0; i < 5; i++)
{
scanf("%c", &s[i]);
}
3) gets
#include <stdio.h>
char *gets(char *s);
功能:从终端获取字符串
参数:s:目标字符数组的首地址
返回值:目标字符数组的首地址
gets(s); // gets在输入的时不关心数组越界问题,使用时会报警告
char s[32] = {};
gets(s);
printf("%s\n", s);
输出
1) printf("%s\n", s);
2) 循环遍历
for(int i = 0; i < 32; i++){
printf("%c", s[i]);
}
3) puts
#include <stdio.h>
int puts(const char *s);
功能:向终端输出字符串
参数:s:要输出字符数组的首地址
返回值:输出字符的个数
// 如果不需要返回值直接调用函数就可以了
puts(s);
char s[32] = {};
gets(s);
puts(s);
例题:判断对错
char s[10] = {};
s = "hello"; // 错误,s是地址常量,不能为左值
s[10] = "hello"; // 错误,数组越界
不能直接将字符串常量赋值给字符数组的一个元素。字符串常量(例如
"hello"
)是一个字符数组,不能直接赋值给单个字符数组元素。正确的做法是使用字符串复制函数,例如strcpy
。strcpy(s, "string"); // 正确
练习:
将一串字符串进行倒置,例如:char buf[32]=”hello”;//olleh
int n;
for(n = 0; buf[n] != '\0', n++); // 计算元素的个数
#include <stdio.h>
int main(int argc, char const *argv[])
{
char buf[]="hello";
int n=sizeof(buf)-1;
int temp;
for(int i=0;i<n/2;i++){
temp=buf[n-i-1];
buf[n-i-1]=buf[i];
buf[i]=temp;
}
printf("%s\n",buf);
//puts(buf);
return 0;
}
计算字符串的实际长度
1. for循环遍历数组,直到 \0 为止
for (n = 0; buf[n] != '\0'; n++);
2. strlen
#include <string.h>
size_t strlen(const char *s);
功能:计算字符串的实际长度(不包含 \0)
参数:s:要计算字符串的首地址
返回值:字符串的实际长度
char buf[32] = {"hello"};
int num = strlen(buf);
printf("%d\n", num); // 5
sizeof(buf); // 32
char buf[] = {"hello"};
int num = strlen(buf);
printf("%d\n", num); // 5
sizeof(buf); // 6
sizeof和strlen的区别?
// 本质上
1. sizeof 是关键字,strlen是函数
// 功能上
2. sizeof是计算数据所占内存空间大小
strlen计算字符串的实际长度(字符的数量,不包括结束标志'\0')
// 使用时
3. sizeof计算包括 \0,strlen是不包含 \0;计算字符串长度时(元素个数省略的情况下)sizeof 比 strlen大1;
练习:实现字符串大小写转换
#include <stdio.h>
int main(int argc, char const *argv[])
{
char buf[32]={};
scanf("%s",buf);
int n=sizeof(buf);
for(int i=0;i<n;i++){
if(buf[i]<=122&&buf[i]>=97){
buf[i]-=32;
}
else if(buf[i]<=90&&buf[i]>=65){
buf[i]+=32;
}
// else
// {
// printf("ERROR");
// }
}
printf("%s\n",buf);
//puts(buf);
return 0;
}
练习:
1. 在终端输入大写字母、小写字母、空格、数字,分别在终端输出他们的个数
2. 猴子吃桃问题:猴子第一天摘下若干个桃子,当即吃了一半,还不瘾,又多吃了一个,第二天早上又将剩下的桃子吃掉一半,又多吃了一个。以后每天早上都吃了前一天剩下的一半零一个。到第10天早上想再吃时,见只剩下一个桃子了。求第一天共摘了多少。
冒泡排序
两两比较,第j个和 j+1个比较
int a[5] = {5, 4, 3, 2, 1};
第一轮: i = 0 n个数:比较n-1轮,每一轮交换的次数是 n - 1次开始依次递减的
4 5 3 2 1
4 3 5 2 1
4 3 2 5 1
4 3 2 1 5
第二轮: i = 1
3 4 2 1 5
3 2 4 1 5
3 2 1 4 5
第三轮: i = 2
2 3 1 4 5
2 1 3 4 5
第四轮: i = 3
1 2 3 4 5
练习:将10个数进行冒泡排序
选择排序
n 个数:先找出最小值的下标暂存,选择出最小的值与arr[i] 个数交换
排序过程:
1. 首先通过n-1次比较,从n个数中找出最小值的下标,将它与第一个数交换,第一轮选择排序,结果最小值被放在第一个元素的位置上
2. 再通过 n-2 次比较,从剩余的 n-1 个数中找出最小值的下标做记录,将它与第二个数交换,第二轮的排序
3. 重复上述过程,共经过 n-1 轮排序后,排序结束
二维数组
1. 格式
存储类型 数据类型 数组名[行数][列数]
int arr[2][3];
2. 访问元素:
数组名[行下标][列下标];(下标从0开始)
arr[0][0]:第一行第一列的元素
arr[1][2]:第二行第三列的元素
行下标和列下标都不能越界
行数可以省略,列数不能省略
int a[][3] = {1, 2, 3, 4, 5, 6};
3. 数组的元素个数
行数*列数
二维数组的大小 / 数据类型的大小
4. 二维数组的大小
sizeof(数组名)
数据类型的大小*行数*列数
5. 数组名
arr: 第一行首地址
arr+1:第二行首地址
arr+n:第n+1行首地址
6. 初始化:
a) 全部初始化:
int arr[2][3] = {1, 2, 3, 4, 5, 6}; // 顺序赋值
printf("%d %d %d\n", arr[0][0], arr[0][1], arr[0][2]); // 1 2 3
printf("%d %d %d\n", arr[1][0], arr[1][1], arr[1][2]); // 4 5 6
int arr[2][3] = {
{1, 2, 3},
{4, 5, 6}
}; // 按行赋值
b) 部分初始化:未被初始化的元素值为0
int arr[2][3] = {1, 2, 3, 4}; // 顺序赋值 1 2 3 4 0 0
int arr[2][3] = {{1, 2},{4, 5}}; // 按行赋值 1 2 0 4 5 0
c) 未初始化:随机值,需要单独赋值
int arr[2][3];
A
C
// B
for(i=0;i<3;i++)
{
for(j=0;j<=i;j++)
{
a[i][j]=i*j;
}
}
printf("%d\n",a[][]);
a[0][0]=0;
a[1][0]=0;a[1][1]=1;
a[2][0]=0;a[2][1]=2;a[2][2]=4;
7. 内存分配
// a 和 a[0] 并不完全相同,级别不同,后面会讲,数值上是一样的,级别不一样
// a 是行地址
// a[0] 是列地址
// a 表示第一行的首地址
// a+1 表示第二行的首地址
// a[0] 表示第一行第一列的地址
// a[1] 表示第二行第一列的地址
8. 二维数组遍历
for循环嵌套,外层行数,内层列数
int a[m][n] = {};
for(int i = 0; i < m; i++) // 行下标
{
for(int j = 0; j < n; j++) // 列下标
{
scanf(); 或者 printf();
}
}
// 写一个 for循环嵌套 a[2][3] 从终端输入,并输出,也可以直接初始化,从终端输出
练习3:有一个3x4的矩阵(元素值不相同),要求输出其中最大值以及它的行号和列号
练习:打印杨辉三角形的前十行
1
1 1
1 2 1
1 3 3 1
1 4 6 4 1
1 5 10 10 5 1
1 6 15 20 15 6 1
if(i == j || j == 0)
a[i][j] = 1;
a[i][j] = a[i-1][j] + a[i-1][j-1]
指针
指针的优点:
\1. 使程序更简洁、紧凑、高效
\2. 有效的表达更复杂的数据结构
\3. 动态分配内存
\4. 得到多于一个数的函数返回值
1. 概念
地址:内存中每个字节单位都有一个编号(门牌号)
指针:指针就是地址
指针变量:用于存放地址的这种变量就叫指针变量
2. 格式:
存储类型 数据类型 *指针变量名
int *p; // 定义了一个指针变量p
例子:
int num = 5;
int *p = #
char ch = 'c';
char *q = &ch;
3. 指针操作符:
&:取地址符,取变量的地址
*:取内容,取地址里面的内容
*&a == a; // *和&都是单目运算符,互逆运算
&*a; // 运算符的优先级 // a是变量就是错的,a是地址就是对的
4. 初始化:
指针变量在使用前不仅要定义还要初始化,未初始化的指针变量不能随便使用,会产生野指针
4.1. 将普通变量的地址赋值给指针变量
int a = 10;
1) int *p = &a; // 在定义的同时赋值
2) int *p = NULL;
p = &a; // 先定义后赋值
#include <stdio.h>
int main(int argc, char const *argv[])
{
int a = 10;
int *p = NULL;
p = &a;
printf("%d %d\n", a, *p); // 打印a的值 10 10
printf("%p %p\n", &a, p); // 打印a的地址
*p = 3; // 可以通过*p改变指向的内容
printf("%d %d\n", a, *p); // 打印a的值 10 10
printf("%p %p\n", &a, p); // 打印a的地址
return 0;
}
练习:
1. 键盘输入10个数,输出个位数是奇数或者十位数是偶数的所有数
2. 键盘输入数值 按照个位数升序的顺序排序
4. 初始化
4.2. 将数组的首地址赋值给指针变量
char str[10] = "hello";
char *p = str; // 指针指向了数组的首地址,即指向了字符 'h'
char str[10] = "hello";
char *p = str;
printf("%p %p\n", p, str);
printf("%c %c\n", *p, *str);
4.3. 将指针变量里面保存的地址赋值给另一个指针变量
float a =1.3;
float *p = &a;
float *q = p;
5. 指针运算
5.1. 算术运算
char str[32] = "hello";
char *p = str;
// 加一,不是加了一个字节,而是一个数据单位
p++; // 指针向高地址方向移动一个数据单位,指针指向发生变化
p--; // 指针向低地址方向移动一个数据单位,指针指向发生变化
例子:
int *p; p++; // 移动4个字节
double *p; p++; // 移动8个字节
char str[10] = "hello";
char *p = str;
printf("%p %p\n", &str[0], &str[1]);
p++;
printf("%p\n", p);
printf("%c %c\n", *p, *str);
p+n; 向高地址方向访问第n个数据的地址,指针指向不发生变化
p-n; 向低地址方向访问第n个数据的地址,指针指向不发生变化
偏移了多少地址(字节) = n * sizeof(数据类型)
两个地址之间的差 == 两个地址之间相隔的元素个数
q - p = 之间相隔的元素个数
int m = 100;
double n = 200;
int *p1 = &m;
int *p2 = p1 + 2;
double *q1 = &n;
double *q2 = q1 + 2;
printf("p1=%p p2%p\n", p1, p2);
printf("p2 - p1 = %d\n", p2-p1);
printf("q1=%p q2%p\n", q1, q2);
printf("q2 - q1 = %d\n", q2-q1);
5.2. 关系运算
> < == !=
指针之间的关系运算比较的是它指向地址的高低
指向高地址的指针大于指向低地址的指针
char str[32] = "hello";
char *p1 = &str[1];
char *p2 = &str[3];
p2 > p1;
注意:(同一个数组之间进行比较)
指向不同类型数组的指针进行关系运算时没有意义,
指向不同区域的指针的关系运算也没有意义
6. 指针的大小
int a = 5;
int *p1 = &a; // 4
short b = 2;
short *p2 = &b; // 4
double c = 1.11;
double *p3 = &c; // 4
double *p4 = NULL; // 4
printf("%d\n", sizeof(p4));
32位操作系统:指针大小为4字节
8位16进制表示,4字节
64位操作系统:指针大小为8字节
16位16进制表示,8字节
总结:
1. 32位操作系统:指针大小为4字节,64位操作系统,指针大小为8字节
2. 内存地址是固定,但是但是变量的地址不固定(栈区随机分配变量)
3. 指针类型根据指针指向空间的数据类型
练习:
编写一个程序实现功能:将字符串“Computer Science”赋值给一个字符数组,然后从第一个字母开始间隔的输出该字符串,用指针完成。结果:Cmue cec
Segentation fault (core dumped
产生原因:
访问不存在的内存地址、访问系统保护的地址、访问只读的内存地址、
空指针废弃(eg:malloc 与 free释放后,继续使用)、栈堆溢出
内存越界(数组越界、变量类型不一致等)
练习:
1. 字符串倒置(用指针实现) 如:hello->olleh
思路:定义两个指针分别指向字符串的开头和结尾,前面的指针向后走,后面的指针向前走
2. 将字符串转换成整型数字输出。用指针实现
要求:字符串为0-9组成,输出数据为一个整形数
Eg:char s[10]=”123”;printf(“%d\n”,num); //num=123;
指针修饰
1. const 常量化
1) const int a =10;
int const a = 10;
a = 20; // 错,a的值不能改变,可以通过指针间接修改
// 因为 const 没有修饰 *p,修饰的是a
int *p = &a;
*p = 20; // 可以
2) const int *p; // 修饰 *p,指针指向的内容不能修改,指针指向可以修改
int const *p;
int a = 10;
const int *p = &a;
2.1) *p = 20; // 错误,因为 *p 被修饰
2.2) int b = 20;
p = &b; // 正确
3) int *const p; // 修饰 p,指针指向不能被改变,指针指向的内容可以修改
int a = 10;
int b = 20;
int *const p = &a;
3.1) *p = 20; // 正确
3.2) p = &b; // 错误,因为修饰的是 p,指针指向不能改变
4) 修饰函数的参数
2. void
void a; // 不允许修饰变量
void *p; // p 任意类型的指针
使用场景:函数参数或者函数返回值
注意:通过void类型的指针进行取内容的时候,需要对地址进行强转
转换方式:void *p = NULL; 强转:(int *)p 取内容:*(int *)p
int a = 10;
void *p = NULL;
// (int *)p = &a; // 错误 对指针进行类型强转需要在赋值右面
p = &a;
int *q = (int *)p;
printf("%d %d\n", *q, *(int *)p); // 10 10
1. 若有以下说明语句:int a[12]={1,2,3,4,5,6,7,8,9,10,11,12};char c='a', d, g;则数值为4的表达式是()。
A)a[g-c] B)a[4] C)a['d'-'c'] D) a['d' - c]
2. 假设int型变量占两个字节的存储单元,若有定义:int x[10]={0,2,4};则数组x在内存中所占字节数为()。
A)40 B)6 C)12 D) 20
3. 若给出定义:char x[]=”abcdefg”;char y[]={‘a’,’b’,’c’,’d’,’e’,’f’,’g’};则正确的叙述为()。
A) 数组x和数组y等价 B) 数组x和数组y的长度相同
C) 数组x的长度大于数组y的长度 D) 数组y的长度大于数组x的长度
4. 有以下程序,执行后输出结果是()。
#include<stdio.h>
int main()
{
int m[][3]={1,4,7,2,5,8,3,6,9};
int i,j,k=2;
for(i=0; i<3; i++)
{
printf("%d ",m[k][i]);
}
}
A 4 5 6 B) 2 5 8 C) 3 6 9 D) 7 8 9
5. 若有以下定义语句:char s[10];s=”abcd”;printf(“%s\n”,s);则运行结果是()。
A) 输出 abcd B) 输出a C) 输出 ab cd D) 编译不通过
6. 若有以下程序段,该程序段的输出结果是()。
char str[]=“ab\n\012\\\””;
printf(“%d”,strlen(str));
A) 3 B) 7 C) 6 D) 12
大小端
在计算机进行超过1字节数据进行存储时,会出现存储数据顺序不同的情况即大小端存储
Big-Endian(大端字节序)大端:在低地址存放高字节数据,高地址存放低字节数据
Little-Endian(小端字节序) 小端:在低地址存放低字节数据,在高地址存放高字节数据
举例:存储数据:0x12345678, 起始地址:0x4000
0x4000 0x4001 0x4002 0x4003
大端: 0x12 0x34 0x56 0x78
小端: 0x78 0x56 0x34 0x12
int a = 0x12345678;
char b;
b = (char)a;
printf("%#x\n", b);
// 电脑是小端,网络是大端
// 电脑向网络传输东西,数据要从小端转成大端,传出去
// 网络像电脑传输东西,数据要从大端转成小端,接收回来
二级指针
一级指针:存放变量的地址
二级指针:存放一级指针的地址(指针变量的地址)
格式:存储类型 数据类型 **指针变量名
int a = 10;
int *p = &a;
int **q = &p;
访问a的值:
a *p **q
访问a的地址:
&a p *q
访问 p的地址
&p q
标签:arr,int,基础,笔记,C语言,char,数组,printf,指针 From: https://blog.csdn.net/weixin_58298518/article/details/139997225