C语言进阶21收尾(编程练习)(atoi,strncpy,strncat,offsetof模拟实现+找单身狗+宏交换二进制奇偶位)(下)

简介: C语言进阶21收尾(编程练习)(atoi,strncpy,strncat,offsetof模拟实现+找单身狗+宏交换二进制奇偶位)

C语言进阶21收尾(编程练习)(atoi,strncpy,strncat,offsetof模拟实现+找单身狗+宏交换二进制奇偶位)(上):https://developer.aliyun.com/article/1513286


4 找一个单身狗

【题目内容】

一个数组中只有一个数字是出现一次,其他所有数字都出现了两次。

编写一个函数找出这两个只出现一次的数字。

解析:

例如数组 1 2 2 3 3 4 1中 4就是单身狗

思路 因为单身狗只有一个 直接全部异或

异或的特点相同为0,相异为1,

如果有重复的数字 异或就等于0 最后只会留一下一个单身狗

看代码:

 
#include<stdio.h>
#include<string.h>
int find_dog(int arr[],int sz)
{
    int dog = 0;
    for (int i = 0;i < sz;i++)
    {
        dog ^= arr[i];
    }
    return dog;
}
int main()
{
    int arr[] = { 1,2,2,3,3,4,1 };
    int sz = sizeof(arr) / sizeof(arr[0]);
    printf("单身狗是%d", find_dog(arr,sz));
    return 0;
}


5 找两个单身狗

【题目内容】

一个数组中只有两个数字是出现一次,其他所有数字都出现了两次。

编写一个函数找出这两个只出现一次的数字。

解析:

如果我们按照之前的思路直接异或 肯定只会出来一个四不像数,

假设数组1 2 3 3 1 4,

我们把 两个单身狗分成两个组,

而组中其他的数字就都不是单身狗,

此时我们在分组异或就分别得到了2个单身狗,

问题是我们以什么为依据分组?

依据 二进制位 异或把相同的数字变成0,不同的数字变成1,

我们根据1在哪位 就说明单身狗这个的二进制位不同 ,按照这个二进制位分,

两个单身狗是不可能进到一组的

我们依然把数组中所有数字异或到一起 然后判断这个数字的二进制位 因为有两个单身狗2和4

0010 和0100最后异或完毕得到的二进制位是 0110 说明两个单身狗数字的二进制最后位是相等

我们左移一位得到了1 就说明 两个单身狗数字的倒数第二位二进制数 不相等

让数组中所有的数字左移一位 如果等于 1 放进第一个数组中

如果等于0 放进第二个数组中

把数组中的数字全部异或就得到了 2个单身狗

代码:

 
#include<stdio.h>
void find_two_dog(int arr[],int sz,int* px,int* py)
{
    int sum = 0;
    for (int i = 0; i < sz; i++)
    {
        sum ^= arr[i];
    }
    int count = 0;
    for (int i = 0; i < 32; i++)
    {
        if (sum & 1 << i) //循环判断第几位是1
        {
            count = i;//如果是1 记录下来
            break;
        }
    }
    int dog1 = 0, dog2 = 0;
    for (int i = 0; i < sz; i++)
    {
        if (arr[i] & 1 << count)
        {
            dog1 ^= arr[i];
        }
        else
        {
            dog2 ^= arr[i];
        }
    }
    *px = dog1;
    *py = dog2;
}
int main()
{
    int arr[] = { 1,2,3,3,1,4 };
    int sz = sizeof(arr) / sizeof(arr[0]);
    int x = 0, y = 0;//因为不能return两个值,所以设置两个返回型参数
    find_two_dog(arr, sz, &x, &y);
    printf("第一个单身狗%d\n第二个单身狗%d",x, y);
    return 0;
}


6 宏交换二进制奇偶位

【题目内容】

写一个宏,可以将一个整数的二进制位的奇数位和偶数位交换。

解题思路:

做交换,直接将二进制位中奇数和偶数部分分别全部提取出来,交换一下位置,

再重新以相加或者位运算或的方式求和,即可得到想要的结果。

首先要做的是:分别提取给出整数的奇数位和偶数位,即:

按从右往左数第一位是1(奇数)

提取奇数位:(N) & 01010101010101010101010101010101 (0x55555555)

提取偶数位:(N) & 10101010101010101010101010101010 (0xaaaaaaaa)

将奇数位左移一位,偶数位右移一位。

相加移位后结果(或者位运算或)。

 
#include<stdio.h>      //a  1010                   5  0101
#define SWAP(R) (((R) & 0xaaaaaaaa)>>1) + (((R) & 0x55555555)<<1)
int main()
{
    int a = 10;
    printf("%d\n", SWAP(a));
    return 0;
}


7 offsetof宏的模拟实现

写一个宏,计算结构体中某变量相对于首地址的偏移,并给出说明。

考察:offsetof宏的实现。

offsetof介绍:


格式:offsetof(type, member) 头文件:<stddef.h>功能:返回成员 相对于结构或联合 起始地址的偏移量(以 字节为单位),返回类型是size_t。

这个宏有两个参数:

type 是一个结构体类型或联合类型;member 是结构体或联合的某一个成员

使用代码:

 
#include <stdio.h>
#include <stddef.h>
struct S
{
    int a;//0 1 2 3
    char b;//4
    //5
    short c;//6  7
};
int main()
{
    printf("%d\n", offsetof(struct S, a));
    printf("%d\n", offsetof(struct S, b));
    printf("%d\n", offsetof(struct S, c));
    return 0;
}

模拟实现:

结构体起始地址加上该成员相对于起始地址的偏移量就是成员变量的地址。

想要知道成员相对于结构体起始地址的偏移量,假设结构体起始地址位于0地址处,

那么结构体成员变量的地址强制类型转换为size_t后就相当于该成员相对于起始地址的偏移量。

 
#include <stdio.h>
#include <stddef.h>
//结构体起始地址加上该成员相对于起始地址的偏移量就是成员变量的地址。
//想要知道成员相对于结构体起始地址的偏移量,假设结构体起始地址位于0地址处,
//那么结构体成员变量的地址强制类型转换为size_t后就相当于该成员相对于起始地址的偏移量
#define OFFSETOF(type, member) (size_t)(&(((type*)0)->member))//-0忽略了
struct S 
{
    int a;//0 1 2 3
    char b;//4
    //5
    short c;//6  7
};
int main() 
{
    printf("%d\n", offsetof(struct S, a));
    printf("%d\n", offsetof(struct S, b));
    printf("%d\n", offsetof(struct S, c));
 
    printf("%d\n", OFFSETOF(struct S, a));
    printf("%d\n", OFFSETOF(struct S, b));
    printf("%d\n", OFFSETOF(struct S, c));
    return 0;
}


C语言完结撒花

下个专栏是C语言实现初阶数据结构吧 image.gif






目录
相关文章
|
1月前
|
NoSQL C语言 索引
十二个C语言新手编程时常犯的错误及解决方式
C语言初学者常遇错误包括语法错误、未初始化变量、数组越界、指针错误、函数声明与定义不匹配、忘记包含头文件、格式化字符串错误、忘记返回值、内存泄漏、逻辑错误、字符串未正确终止及递归无退出条件。解决方法涉及仔细检查代码、初始化变量、确保索引有效、正确使用指针与格式化字符串、包含必要头文件、使用调试工具跟踪逻辑、避免内存泄漏及确保递归有基准情况。利用调试器、编写注释及查阅资料也有助于提高编程效率。避免这些错误可使代码更稳定、高效。
228 12
|
2月前
|
存储 算法 Linux
C语言 多进程编程(一)进程创建
本文详细介绍了Linux系统中的进程管理。首先,文章解释了进程的概念及其特点,强调了进程作为操作系统中独立可调度实体的重要性。文章还深入讲解了Linux下的进程管理,包括如何获取进程ID、进程地址空间、虚拟地址与物理地址的区别,以及进程状态管理和优先级设置等内容。此外,还介绍了常用进程管理命令如`ps`、`top`、`pstree`和`kill`的使用方法。最后,文章讨论了进程的创建、退出和等待机制,并展示了如何通过`fork()`、`exec`家族函数以及`wait()`和`waitpid()`函数来管理和控制进程。此外,还介绍了守护进程的创建方法。
C语言 多进程编程(一)进程创建
|
2月前
|
Linux C语言
C语言 多进程编程(三)信号处理方式和自定义处理函数
本文详细介绍了Linux系统中进程间通信的关键机制——信号。首先解释了信号作为一种异步通知机制的特点及其主要来源,接着列举了常见的信号类型及其定义。文章进一步探讨了信号的处理流程和Linux中处理信号的方式,包括忽略信号、捕捉信号以及执行默认操作。此外,通过具体示例演示了如何创建子进程并通过信号进行控制。最后,讲解了如何通过`signal`函数自定义信号处理函数,并提供了完整的示例代码,展示了父子进程之间通过信号进行通信的过程。
|
2月前
|
Linux C语言
C语言 多进程编程(四)定时器信号和子进程退出信号
本文详细介绍了Linux系统中的定时器信号及其相关函数。首先,文章解释了`SIGALRM`信号的作用及应用场景,包括计时器、超时重试和定时任务等。接着介绍了`alarm()`函数,展示了如何设置定时器以及其局限性。随后探讨了`setitimer()`函数,比较了它与`alarm()`的不同之处,包括定时器类型、精度和支持的定时器数量等方面。最后,文章讲解了子进程退出时如何利用`SIGCHLD`信号,提供了示例代码展示如何处理子进程退出信号,避免僵尸进程问题。
|
2月前
|
消息中间件 Unix Linux
C语言 多进程编程(五)消息队列
本文介绍了Linux系统中多进程通信之消息队列的使用方法。首先通过`ftok()`函数生成消息队列的唯一ID,然后使用`msgget()`创建消息队列,并通过`msgctl()`进行操作,如删除队列。接着,通过`msgsnd()`函数发送消息到消息队列,使用`msgrcv()`函数从队列中接收消息。文章提供了详细的函数原型、参数说明及示例代码,帮助读者理解和应用消息队列进行进程间通信。
|
2月前
|
缓存 Linux C语言
C语言 多进程编程(六)共享内存
本文介绍了Linux系统下的多进程通信机制——共享内存的使用方法。首先详细讲解了如何通过`shmget()`函数创建共享内存,并提供了示例代码。接着介绍了如何利用`shmctl()`函数删除共享内存。随后,文章解释了共享内存映射的概念及其实现方法,包括使用`shmat()`函数进行映射以及使用`shmdt()`函数解除映射,并给出了相应的示例代码。最后,展示了如何在共享内存中读写数据的具体操作流程。
|
2月前
|
消息中间件 Unix Linux
C语言 多进程编程(二)管道
本文详细介绍了Linux下的进程间通信(IPC),重点讨论了管道通信机制。首先,文章概述了进程间通信的基本概念及重要性,并列举了几种常见的IPC方式。接着深入探讨了管道通信,包括无名管道(匿名管道)和有名管道(命名管道)。无名管道主要用于父子进程间的单向通信,有名管道则可用于任意进程间的通信。文中提供了丰富的示例代码,展示了如何使用`pipe()`和`mkfifo()`函数创建管道,并通过实例演示了如何利用管道进行进程间的消息传递。此外,还分析了管道的特点、优缺点以及如何通过`errno`判断管道是否存在,帮助读者更好地理解和应用管道通信技术。
|
2月前
|
存储 Ubuntu Linux
C语言 多线程编程(1) 初识线程和条件变量
本文档详细介绍了多线程的概念、相关命令及线程的操作方法。首先解释了线程的定义及其与进程的关系,接着对比了线程与进程的区别。随后介绍了如何在 Linux 系统中使用 `pidstat`、`top` 和 `ps` 命令查看线程信息。文档还探讨了多进程和多线程模式各自的优缺点及适用场景,并详细讲解了如何使用 POSIX 线程库创建、退出、等待和取消线程。此外,还介绍了线程分离的概念和方法,并提供了多个示例代码帮助理解。最后,深入探讨了线程间的通讯机制、互斥锁和条件变量的使用,通过具体示例展示了如何实现生产者与消费者的同步模型。
|
2月前
|
Linux C语言
C语言 多进程编程(七)信号量
本文档详细介绍了进程间通信中的信号量机制。首先解释了资源竞争、临界资源和临界区的概念,并重点阐述了信号量如何解决这些问题。信号量作为一种协调共享资源访问的机制,包括互斥和同步两方面。文档还详细描述了无名信号量的初始化、等待、释放及销毁等操作,并提供了相应的 C 语言示例代码。此外,还介绍了如何创建信号量集合、初始化信号量以及信号量的操作方法。最后,通过实际示例展示了信号量在进程互斥和同步中的应用,包括如何使用信号量避免资源竞争,并实现了父子进程间的同步输出。附带的 `sem.h` 和 `sem.c` 文件提供了信号量操作的具体实现。
|
3月前
|
传感器 数据采集 API
C语言与硬件编程:GPIO操作
C语言与硬件编程:GPIO操作
143 0