【C语言篇】C语言常考及易错题整理DAY2

本文涉及的产品
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: p是一个指针数组,p[i] = &a[i*3]相当于是把数组a每3个一组分开并把每组的首地址存在p数组,此时p类似一个4行3列的二 维数组,p[3][2]就是4行第3个元素12。

C语言常考及易错题整理


选择题


  1. 下列 for 循环的次数为( )
for(int i = 0 ; i || i++ < 5;)

A: 0


B: 5


C: 1


D: 无限


答案解析:


正确答案:D


2.逻辑或运算如果前表达式为真,后表达式不计算,第一次循环时i为0,执行i++,第二次循环时i为1,是个真值,不再执行 i++,也就死循环了


在c语言中,一个函数不写返回值类型,默认的返回类型是( )

A: int


B: char


C: void


D: 都不是


答案解析:


正确答案:A


一个函数不写返回值类型,默认的返回类型是int,但不提倡这么做


相关知识:【C语言篇】从零带你全面了解函数(包括隐式声明等)


在上下文及头文件均正常的情况下,下列代码的输出是( )(注: print 已经声明过)


int main()
{
    char str[] = "Geneius";
    print(str);
    return 0;
}
print(char *s)
{
    if(*s)
    {
        print(++s);
        printf("%c", *s);
    }
}

答案解析:


正确答案:suiene


代码实现了递归倒序打印字符串的功能,但是++s使得s的值发生了变化,回不到'G'的位置上,故而没有打印'G'


  1. 下列程序的输出是:
#include <stdio.h>
int main()
{
    int a [12]= {1,2,3,4,5,6,7,8,9,10,11,12},*p[4],i;
    for(i=0;i<4;i++)
        p[i]=&a [i*3];
    printf("%d\n",p[3][2]);
    return 0;
}

答案解析:


正确答案:12


p是一个指针数组,p[i] = &a[i*3]相当于是把数组a每3个一组分开并把每组的首地址存在p数组,此时p类似一个4行3列的二 维数组,p[3][2]就是4行第3个元素12


5.二维数组X按行顺序存储,其中每个元素占1个存储单元。若 X[4][4] 的存储地址为 Oxf8b82140 , X[9][9] 的存储地 址为 Oxf8b8221c ,则 X[7][7] 的存储地址为( )

A: Oxf8b821c4


B: Oxf8b821a6


C: Oxf8b82198


D: Oxf8b821c


答案解析:


正确答案:A


假设每行有n个元素:那x[9][9]元素的地址 - x[4][4]元素的地址 = 0x21c-0x140=5n+5(21c和140是地址末三位的十六进制数),这里n是43,假设``x[7][7]的地址是z,x[7][7]元素的地址 - x[4][4]`元素的地址 = z-0x140 = 3n+3,z = 3n+3+140 = 3*43+3+0x140 = 0x84+0x140 = 0x1c4,看地址的尾数,选择A


6.求函数返回值,传入 -1 ,则在64位机器上函数返回( )


int func(int x)
{
    int count = 0;
    while (x)
    {
        count++;
        x = x&(x - 1);//与运算
    }
    return count;
}

答案解析:


正确答案:32


x=x&(x-1)这个表达式执行一次就会将x的2进制中最右边的1去掉,在x变成0之前,表达式能执行几次,就去掉几个1,所以这 个代码实现了求一个有符号整数二进制补码中1的个数的功能,我们知道-1的补码是全1,而int类型4个字节32位


7.有以下代码,会出现什么结果:

int count = 0;
int x = -1;
while(x)
{
    count++;
    x = x >> 1;
}
printf("%d",count);

答案解析:


正确答案:死循环


此题一个关键,有符号数右移一般默认运算高位是补符号位的(算术右移),负数的符号位是1,所以x永远不会变为0,是个死循环


相关知识: 【C语言篇】操作符详解(下篇)


编程题


至少是其他数字两倍的最大数


给你一个整数数组 nums ,其中总是存在 唯一的 一个最大整数 。


请你找出数组中的最大元素并检查它是否 至少是数组中每个其他数字的两倍 。如果是,则返回 最大元素的下标 ,否则返回 -1 。


示例 1:

输入:nums = [3,6,1,0]
输出:1
解释:6 是最大的整数,对于数组中的其他整数,6 至少是数组中其他元素的两倍。6 的下标是 1 ,所以返回 1 。

示例 2:

输入:nums = [1,2,3,4]
输出:-1
解释:4 没有超过 3 的两倍大,所以返回 -1 。

暴力破解:双重循环遍历数组,对每个元素判断是否是其他元素的两倍。或者先遍历一遍找出最大值,然后遍历一遍判断是否是其他数字二倍。


更优思想:一次遍历找出最大的数字和次大的数字,判断最大的数字是否是次大数字2倍即可

int dominantIndex(int* nums, int numsSize){
    if (numsSize == 1) return 0;//特殊情况只有一个元素则特殊处理
    int max, sec, idx;
    //先对最大和次大进行选择赋值,注意max和sec不能随意赋初值,因为有可能你赋予的初值就是最大值
    //因此要使用数组中的数据进行初值赋予。
    if (nums[0] > nums[1]) {
        max = nums[0];
        idx = 0;
        sec = nums[1];
    }else {
        max = nums[1];
        idx = 1;
        sec = nums[0];
    }
    for (int i = 2; i < numsSize; i++) {
        if (nums[i] > max) { //当前元素大于max,则意味着要替换
            sec = max; //先将原先最大的保存到sec中,则他就是次大的
            max = nums[i]; //再将最大的放到max中
            idx = i; //保存max值的下标
        }else if (nums[i] > sec){
            //避免刚好nums[0]就是最大的情况,因为不切换最大而导致次大无法判断情况
            sec = nums[i];
        }
    }
    if (max >= sec * 2) {
        return idx;
    }
    return -1;
}

两个数组的交集


给定两个数组 nums1nums2 ,返回它们的交集。输出结果中的每个元素一定是 唯一 的。我们可以不考虑输出结果的顺序


示例 1:

输入:nums1 = [1,2,2,1], nums2 = [2,2]
输出:[2]

示例 2:

输入:nums1 = [4,9,5], nums2 = [9,4,9,8,4]
输出:[9,4]
解释:[4,9] 也是可通过的

提示:


  • 1 <= nums1.length, nums2.length <= 1000
  • 0 <= nums1[i], nums2[i] <= 1000


哈希表,将两个数组的元素当做哈希数组的下标,若出现则将其下标存储数据置为1,当且仅当两个哈希数组对应下标存储的数据都是1时说明这个元素在两个数组都出现过,返回创建的数组即可

int* intersection(int* nums1, int nums1Size, int* nums2, int nums2Size, int* returnSize){
    int hash1[1001] = {0};
    int hash2[1001] = {0};
    for(int i = 0; i < nums1Size; i++){
        hash1[nums1[i]] = 1;
    }
    for(int i = 0; i < nums2Size; i++){
        hash2[nums2[i]] = 1;
    }
    int *ret = (int*)malloc(sizeof(int)*1001);
    int k = 0;
    for(int i = 0; i < 1001; i++){
        if(hash1[i] == 1 && hash2[i] == 1){
            ret[k++] = i;
        }
    }
    *returnSize = k;
    return ret;
}

图片整理


Lily上课时使用字母数字图片教小朋友们学习英语单词,每次都需要把这些图片按照大小(ASCII码值从小到大)排列收好。请大家给Lily帮忙,通过代码解决。


Lily使用的图片使用字符"A"到"Z"、“a"到"z”、"0"到"9"表示。


数据范围:每组输入的字符串长度满足 1≤n≤1000


输入描述:


一行,一个字符串,字符串中的每个字符表示一张Lily使用的图片。


输出描述


Lily的所有图片按照从小到大的顺序输出


示例1


输入:

Ihave1nose2hands10fingers

输出:

0112Iaadeeefghhinnnorsssv

这道题考察的其实就是字符排序,每个 ascii 字符在内存都有一个对应的 ascii 值,通过内存中数据的存储进行排序就行


冒泡排序:相邻数据之间进行比较交换,将较大的数据向后推到数组末尾,然后开始下一轮次大数据的冒泡过程。

#include <stdio.h>
#include <string.h>
int main() {
    char str[1001] = {0};
    scanf("%s", str);
    int i = 0;
    int j = 0;
    int len = strlen(str);
    for (i = 0; i < len - 1; i++) {
        int flag = 1;
        for (j = 0; j < len - 1 - i; j++) {
            if (str[j] > str[j + 1]) {
                flag = 0;
                char tmp = 0;
                tmp = str[j];
                str[j] = str[j + 1];
                str[j + 1] = tmp;
            }
        }
        if (flag)
            break;
    }
    printf("%s", str);
    return 0;
}

寻找数组的中心下标


给你一个整数数组 nums ,请计算数组的 中心下标 。


数组 中心下标 是数组的一个下标,其左侧所有元素相加的和等于右侧所有元素相加的和。


如果中心下标位于数组最左端,那么左侧数之和视为 0 ,因为在下标的左侧不存在元素。这一点对于中心下标位于数组最右端同样适用。


如果数组有多个中心下标,应该返回 最靠近左边 的那一个。如果数组不存在中心下标,返回 -1 。


示例 1:

输入:nums = [1, 7, 3, 6, 5, 6]
输出:3
解释:
中心下标是 3 。
左侧数之和 sum = nums[0] + nums[1] + nums[2] = 1 + 7 + 3 = 11 ,
右侧数之和 sum = nums[4] + nums[5] = 5 + 6 = 11 ,二者相等。

从数组的0下标处开始向后逐下标统计,计算当前下标左边之和,和右边之和,进行判断,相等则为中心下标,如 果数组循环结束都没有找到中心下标,则返回-1,表示没有中心下标。

int pivotIndex(int* nums, int numsSize){
    int i, j;
    for (i = 0; i < numsSize; i++) {//从假设中心点为0开始进行统计判断
        int l_sum = 0, r_sum = 0;//初始化左边之和和右边之和为0
        for (j = 0; j < numsSize; j++) {

            if (j < i) l_sum += nums[j]; //小于i坐标的都是左边的数字
            else if (j > i) r_sum += nums[j];//大于i坐标的都是右边的数字
        }
        if (l_sum == r_sum) {//如果两遍相等则i就是中心坐标
            return i;
        }
    }
    return -1;
}

优化解法:


先计算总共的和,再遍历


int pivotIndex(int* nums, int numsSize) {
    int total = 0;
    for (int i = 0; i < numsSize; ++i) {
        total += nums[i];
    }
    int sum = 0;
    for (int i = 0; i < numsSize; ++i) {
        if (2 * sum + nums[i] == total) {
            return i;
        }
        sum += nums[i];
    }
    return -1;
}

多数元素


给定一个大小为 n 的数组 nums ,返回其中的多数元素。多数元素是指在数组中出现次数 大于 ⌊ n/2 ⌋ 的元素。

你可以假设数组是非空的,并且给定的数组总是存在多数元素。


示例 1:

输入:nums = [3,2,3]
输出:3

示例 2:

输入:nums = [2,2,1,1,1,2,2]
输出:2

提示:

  • n == nums.length
  • 1 <= n <= 5 * 104
  • -109 <= nums[i] <= 109



**一个数组中有一个数字出现次数大于 n/2 ,从第 0 个字符开始,假设它就是最多的那个数字,遇到相同的数字则 计数 +1 , 遇到不同的则计数 -1 ,其实就是互相消耗,等到计数为 0 的时候,表示本次互拼完毕,从下一个字符重 新开始互拼,但是归根结底出现次数大于 n/2 的这个数字数量更多,因此也是最后保留的字符。 **


示例: “23335” 首先从字符 2 开始计数 1 ,遇到 3 ,不同则 -1 ,互拼消耗重新从剩下的 “335” 开始的过程,这时候保存的字符为 3 ,遇到 3 则计数 +1 , 遇到5则计数 -1 ,在计数不为 0 时,走到末尾保存的字符就是个数超过 n/2 的字符


基于一个很简单的数学原理,当一个数出现次数超过一堆数的一半时,任意相消掉两个不同的数据,这个数在新的这一堆数据中还是保留原来的特性

int majorityElement(int* nums, int numsSize) {
    int count = 1;
    int candidate = nums[0];
    for (int i = 1; i < numsSize; i++) {
        if (nums[i] == candidate)
            count++; else {
                if (count == 0)//前面的数据已经两两相消完了,只用在新的数据中开始继续找即可
                {
                    candidate = nums[i];
                    count=1;
                }
                else
                    count--;
            }
    }
    return candidate;
}

除自身以外数组的乘积

给你一个整数数组 nums,返回 数组 answer ,其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。


题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。


请 不要使用除法且在 O(n) 时间复杂度内完成此题。


示例 1:


输入: nums = [1,2,3,4]
输出: [24,12,8,6]

示例 2:

输入: nums = [-1,1,0,-3,3]
输出: [0,0,9,0,0]

提示:


  • 2 <= nums.length <= 105
  • -30 <= nums[i] <= 30


注意:不要使用除法


将乘积分为两次进行,第一次先将每个位置左边的数据乘积计算出来放到返回数组中,后边第二次循环

将对应位置右边的数据乘积计算出来与返回数组对应位置的左半边乘积相乘得到结果。

示例: 一个数组 int nums[] = {2, 3, 4} 。
int left = 1, right = 1;
计算左侧乘积:
第0个元素的左边乘积, arr[0] = left 然后计算第1位左侧乘积 left*=nums[0] -> left = 1*2
第1个元素的左边乘积, arr[1] = left 然后计算第2位左侧乘积 left*=nums[1] -> left = 1*2*3
第2个元素的左边乘积, arr[2] = left 然后计算第3位左侧乘积 已经没必要了,因为第2元素是末尾元素了
一次循环完毕后,返回数组中每个元素存储的都是自己左侧元素的乘积。 arr[]中的值: [1, 2, 6]
计算右侧乘积:
第2个元素的右边乘积, arr[2] *= right 然后计算第1位右侧乘积 right*=nums[2] -> right =1*4
第1个元素的右边乘积, arr[1] *= right 然后计算第0位右侧乘积 right*=nums[1] -> right =1*4*3
第0个元素的右边乘积, arr[0] *= right 然后计算第-1位右侧乘积 -1位已经不需要计算了
循环完毕后,返回数组中的每个元素都是其他元素的乘积了 arr[2]*=1; arr[1]*=4; arr[0]*=12

int* productExceptSelf(int* nums, int numsSize, int* returnSize){
    int *ret = (int *)malloc(numsSize * sizeof(int));
    *returnSize = numsSize;
    int left = 1, right = 1;
    //第一次循环,将当前位置左边的数字乘积填入返回数组中
    for (int i = 0; i < numsSize; i++) {
        ret[i] = left;// 1 nums[0] nums[0]*nums[1] num[0]*nums[1]*nums[2] ....
        left *= nums[i];
    }
    //第二次循环,对于返回数组的元素从后往前进行,每次乘以右边元素的乘积
    for (int i = numsSize - 1; i >= 0; i--) {
        ret[i] *= right; //最后一个不需要乘以最后元素,乘以1就行
        right *= nums[i]; //right变化:1 nums[end] nums[end]*nums[end-1] .....
    }
    return ret;
}

不使用加减乘除求两个数的加法


写一个函数,求两个整数之和,要求在函数体内不得使用+、-、*、/四则运算符号。


数据范围:两个数都满足 −10≤n≤1000


示例1


输入:

1,2

返回值:

3

示例2


输入:

0,0

返回值:

0

不能使用加减乘除


十进制相加思想: 15+07 , 先计算不考虑进位的相加结果 12 (因为 5+7 的不考虑进位的结果是 2 ,遇 10 进位嘛),然后计算进位 5+7 进位是 10 ,则 10 与 12 再次相加,得到 22 ,进位为 0 ,则计算到此结束。


这里使用二进制求和完成,思想类似,但是二进制计算相加和进位不需要使用 + 符号


二进制相加思想:与十进制相同,先计算不考虑进位的相加结果( 0+0 得 0 , 1+1 进位得 0 , 1+0 得 1 ),使用异或可以取得; 然后计算相加的进位结果(同 1 的位置左移一位即可),使用与运算后左移取得。 示例:


5 0101 + 7 0111
不考虑进位的相加结果 0101^0111 -> 0010
相加的进位 0101&0111 -> 0101 因为进位左移得到 1010
1010 + 0010
不考虑进位的相加结果 1010 ^ 0010 -> 1000
相加的进位 1010 & 0010 -> 0010 因为进位左移得到 0100
1000 + 0100
不考虑进位的相加结果 1000 ^ 0100 -> 1100
相加的进位 1000 & 0100 -> 0000 进位为0结束运算

int Add(int num1, int num2 ) {
    while(num2 != 0) {//进位不为0则持续与相加结果进行相加
        int tmp = num1 ^ num2;//得到每位相加不考虑进位的数据
        num2 = (num1 & num2) << 1;//同1的位相加则会进位
        num1 = tmp;
    }
    return num1;
}

相关文章
|
23天前
|
机器学习/深度学习 存储 C语言
【C语言篇】C语言常考及易错题整理DAY1
swap函数调用时用的是全局变量,主函数中定义的变量只在主函数中有效,因为主函数也是一个函数,它与其他函数是平 行关系;输出语句这里,考虑局部优先的原则。
|
23天前
|
C语言
【C语言篇】C语言常考及易错题整理DAY3
基本数据类型的等级从低到高如下:char int long float double运算的时候是从低转到高的,表达式的类型会自动提升或者转换为参与表达式求值的最上级类型。
|
10月前
|
C语言
C语言分支语句和循环语句经典题及易错题
C语言分支语句和循环语句经典题及易错题
119 0
|
C语言
c语言易错题(下)
c语言易错题(下)
108 0
c语言易错题(下)
|
C语言
c语言易错题(上)
c语言易错题(上)
133 0
c语言易错题(上)
|
2天前
|
存储 C语言
C语言程序设计核心详解 第十章:位运算和c语言文件操作详解_文件操作函数
本文详细介绍了C语言中的位运算和文件操作。位运算包括按位与、或、异或、取反、左移和右移等六种运算符及其复合赋值运算符,每种运算符的功能和应用场景都有具体说明。文件操作部分则涵盖了文件的概念、分类、文件类型指针、文件的打开与关闭、读写操作及当前读写位置的调整等内容,提供了丰富的示例帮助理解。通过对本文的学习,读者可以全面掌握C语言中的位运算和文件处理技术。
|
2天前
|
存储 C语言
C语言程序设计核心详解 第七章 函数和预编译命令
本章介绍C语言中的函数定义与使用,以及预编译命令。主要内容包括函数的定义格式、调用方式和示例分析。C程序结构分为`main()`单框架或多子函数框架。函数不能嵌套定义但可互相调用。变量具有类型、作用范围和存储类别三种属性,其中作用范围分为局部和全局。预编译命令包括文件包含和宏定义,宏定义分为无参和带参两种形式。此外,还介绍了变量的存储类别及其特点。通过实例详细解析了函数调用过程及宏定义的应用。
|
8天前
|
Linux C语言
C语言 多进程编程(三)信号处理方式和自定义处理函数
本文详细介绍了Linux系统中进程间通信的关键机制——信号。首先解释了信号作为一种异步通知机制的特点及其主要来源,接着列举了常见的信号类型及其定义。文章进一步探讨了信号的处理流程和Linux中处理信号的方式,包括忽略信号、捕捉信号以及执行默认操作。此外,通过具体示例演示了如何创建子进程并通过信号进行控制。最后,讲解了如何通过`signal`函数自定义信号处理函数,并提供了完整的示例代码,展示了父子进程之间通过信号进行通信的过程。
|
8天前
|
C语言
C语言 字符串操作函数
本文档详细介绍了多个常用的字符串操作函数,包括 `strlen`、`strcpy`、`strncpy`、`strcat`、`strncat`、`strcmp`、`strncpy`、`sprintf`、`itoa`、`strchr`、`strspn`、`strcspn`、`strstr` 和 `strtok`。每个函数均提供了语法说明、参数解释、返回值描述及示例代码。此外,还给出了部分函数的自实现版本,帮助读者深入理解其工作原理。通过这些函数,可以轻松地进行字符串长度计算、复制、连接、比较等操作。
|
8天前
|
SQL 关系型数据库 C语言
PostgreSQL SQL扩展 ---- C语言函数(三)
可以用C(或者与C兼容,比如C++)语言编写用户自定义函数(User-defined functions)。这些函数被编译到动态可加载目标文件(也称为共享库)中并被守护进程加载到服务中。“C语言函数”与“内部函数”的区别就在于动态加载这个特性,二者的实际编码约定本质上是相同的(因此,标准的内部函数库为用户自定义C语言函数提供了丰富的示例代码)