c语言面试常问问题(二)

数组+指针

数组

1.概念:

具有一定顺序的若干变量的集合

2.定义格式:

存储类型 数据类型 数组名[元素个数]

int arr[5];

数组名:代表数组首元素的首地址,arr是地址常量,不能被赋值,不能为左值

3.访问元素:

数组名[下标]:从0开始

访问第一个元素:arr[0]

访问第n个元素:arr[n-1]

注意数组越界问题

4.特点

  1. 数组元素的数据类型相同
  2. 内存连续
  1. 5.注意
  1. 数组的数据类型就是元素的数据类型
  2. 数组名要符合标识符命名规则
  3. 在同一个函数中,数组名不要与变量名相同
  4. 下标从0开始,到n-1结束
  1. 冒泡排序

两两比较,第j个和j+1个比较

int a[5] = {5 ,4 ,3 ,2 , 1};

第一轮: i = 0 n个数:比较n-1轮,每一轮交换的次数是n-1次开始依次递减的

4 5 3 2 1 j = 0

4 3 5 2 1 j = 1

4 3 2 5 1 j = 2

4 3 2 1 5 j = 3

第二轮: 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

#include <stdio.h>
#include <string.h>
#define N 5

int main(int argc, char const *argv[])
{
    int arr[N] = {5, 4, 3, 2, 1};
    int temp;  // 用于交换的中间变量

    for (int i = 0; i < N - 1; i++)
    {
        for (int j = 0; j < N - 1 - i; j++)
        {
            if(arr[j] > arr[j+1])
            {
                temp = arr[j];
                arr[j] = arr[j+1];
                arr[j+1] = temp;
            }
        }
    }

    for (int i = 0; i < N; i++)
    {
        printf("%d ", arr[i]);
    }
    printf("\n");

    return 0;
}

选择排序

n个数:下找出最小值下标暂存,选择出最小的值与arr[i]交换

排序过程:

  1. 首先通过n-1次比较,从n个数中找出最小值的下标,将它与第一个数交换,第一轮选择排序,结果最小值被放在第一个元素的位置上
  2. 再通过n-2次比较,从剩余的n-1个数中找出最小值的下标做记录,将它与第二个数交换,第二轮排序
  3. 重复上述过程,共经过n-1轮排序后,排序结束

#include <stdio.h>
#define N 5

int main(int argc, char const *argv[])
{
    int arr[N] = {1, 3, 4, 5, 2};
    int temp;  // 用于交换的中间变量
    int k;

    for (int i = 0; i < N - 1; i++)
    {
        k = i;
        for (int j = i + 1; j < N; j++)
        {
            if (arr[j] < arr[k])
            {
                k = j;
            }
        }

        if (i != k)
        {
            temp = arr[k];
            arr[k] = arr[i];
            arr[i] = temp;
        }
    }

    for (int i = 0; i < N; i++)
    {
        printf("%d ", arr[i]);
    }
    printf("\n");
    return 0;
}

  1. 字符数组

  1. 概念

元素的数据类型为字符型的数组

2.形式

char c[] = {'a', 'b', 'c'}; // 逐个字符的赋值,sizeof(c) == 3

// 因为字符串后面会默认补一个\0,所以是6字节

char str[] = {"hello"}; // 用字符串赋值, sizeof(str) == 6

char str[] = "hello"; // 用字符串赋值, sizeof(str) == 6

char str[32] = "hello"; // sizoef(str) == 32

char c[]; // 这样是不可以的,会报一个没有数组大小

char c[] = {}; // 大小是,随便放一个东西就会越界

注意:字符串赋值常常省略数组的长度,需要注意数组越界问题

3.字符数组输入输出

输入

char s[32] = {};

  1. scanf("%s", s);

*输入字符串不能含有空格*,因为scanf输入字符串遇到空格或者 \n 都会认为字符串输入结束,空格后面就不能再存放到数组里面

如果需要输入空格就按以下格式输入

scanf("%[^\n]", s); // 直到输入\n才结束

4.循环遍历

5.gets

#include <stdio.h>

char *gets(char *s);

功能:从终端获取字符串

参数:s:目标字符数组的首地址

返回值:目标字符数组的首地址

输出
  1. printf("%s\n", s);
  2. 循环遍历
  3. puts

#include <stdio.h>

int puts(const char *s);

功能:向终端输出字符串

参数:要输出字符数组的首地址

返回值:输出字符的个数

指针

指针的优点:

\1. 使程序更简洁、紧凑、高效

\2. 有效的表达更复杂的数据结构

\3. 动态分配内存

\4. 得到多于一个数的函数返回值

1.概念

地址:内存中每个字节单元都有的一个编号(门牌号)

指针:指针就是地址

指针变量:用于存放地址的这种变量就叫做指针

2.格式

存储类型 数据类型 *指针变量名;

3.指针操作符

&:取地址符,取变量的地址

*:取内容,取地址里面的内容

*&a == a // *和&都是单目运算符,互逆运算

4.指针运算

4.1. 算术运算

char str[10] = "hello";

char *p = str;

// 加一,不是加了一个字节,而是一个数据单位

p++; // 指针向地址方向移动一个数据单位,指针指向发生改变

p--; // 指针向地址方向移动一个数据单位,指针指向发生改变

例子:

int *p; p++; // 移动4字节

double *p; p++; // 移动8字节

4.2. 关系运算

> < == !=

指针之间的关系原酸比较的是地址的高低

指向高地址的变量大于指向低地址的变量

char str[32] = "hello";

char *p1 = &str[1];

char *p2 = &str[3];

p2 > p1

注意:指向不同类型数组的指针进行关系运算时没有意义,

指向不同区域的指针的关系运算也没有意义

(同一个数组之间进行比较)

总结

  1. 32位操作系统:指针大小为4字节,64位操作系统,指针大小为8字节
  2. 内存地址是固定,但是变量的地址不固定(栈区随机分配变量)
  3. 指针类型根据指针变量指向空间的数据类型(指针变量的数据类型取决于它保存的地址是什么样子的)

指针数组、

本质是数组,里面存放的是指针
格式:存储类型    数据类型    *数组名[元素个数]
例:int a = 10, b = 20, c = 30;
int *arr[3] = {&a, &b, &c};
访问b的地址:
arr[1]        *(arr+1)
访问b的值:
*(arr[1])        *(*(arr+1))

数组指针、

定义:本质上是指针,指向的二维数组(行指针)

格式:存储类型 数据类型 (*指针变量名)[列数];

int a[2][3] = {1, 2, 3, 4, 5, 6};

int (*p)[3] = a;

p可以代替a进行元素访问,但是本质不同

访问 a[i][j] 地址:

*(p+i)+j == *(a+i)+j

p[i]+j == a[i]+j

访问a[i][j] 的内容:

*(*(p+i)+j) *(p[i]+j) p[i][j]

数组指针大小:

sizeof(p); // 8字节

段错误、

当程序尝试访问它没有权限访问的内存地址时容易出现段错误

段错误通常是由以下几种情况引起的:

  1. 访问空指针: 当程序试图访问一个空指针所指向的内存地址时,就会发生段错误。例如,未初始化的指针或者指向已释放的内存的指针。
  2. 数组越界: 当程序试图访问数组越界的元素时,就会发生段错误。例如,访问数组下标小于 0 或者大于等于数组大小的元素。
  3. 非法内存访问: 当程序试图访问未分配的内存区域,或者试图访问系统保留的内存区域,就会发生段错误。
  4. 对只读内存的写操作:当程序试图对只读内存进行写操作时,例如对常量字符串进行修改,就会发生段错误。

要解决段错误问题,可以通过以下几种方式:

方式一:

在程序中加一些打印调试语句,确定段错误的位置,再根据产生错误的原因进行分析具体问题

  1. 检查空指针: 在使用指针之前,先确保指针不是空指针。可以使用条件语句或者断言来检查指针是否为空。
  2. 数组边界检查: 在访问数组元素之前,先确保访问的下标在合法的范 围内。
  3. 动态内存管理: 在动态分配内存(例如使用 malloc、calloc、new m等函数)后,要确保在不再使用该内存时释放它(例如使用 free、delete 等函数)。
  4. 避免对只读内存进行写操作: 对于常量字符串或者是只读内存区域,避免对其进行写操作,可以使用 const 关键字来声明指向只读内存的 指针。

方式二:

使用工具检查: 可以使用一些工具,如gdb、rdynamic、Valgrind等,在程序运行时检查内存错误,包括段错误、内存泄漏等。

空指针

空指针(Null Pointer)是一个指向不存在内存地址的指针。

野指针

野指针(Wild Pointer): 野指针是指指向未知或者无效内存地址的指针。野指针通常产生于以下几种情况:

  • 指针未初始化:指针没有初始化,即没有赋予合法的地址,指针的初始值是随机的。
  • 内存释放后未置空:指针指向的内存已经被释放,指针没有置为 nullptr 或 NULL。
  • 指针越界访问:‌指针指向的内存已经被释放,但程序继续使用该指针。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值