首页 > 其他分享 >C语言【面试】常用知识点总结之常用易错易混点解析

C语言【面试】常用知识点总结之常用易错易混点解析

时间:2024-07-25 15:01:04浏览次数:13  
标签:知识点 常用 数组 int 易错 char str include 指针

第二部分:程序代码评价或者找错

有符号整型和无符号整型混合运算时,有符号型自动转换成无符号型,运算的结果是无符号的。

如果参与运算的数据类型不同,会自动转化为同一类再运算,这就是自动转换

自动转换的规则如下:

1.当参与运算的数据的类型不同时,编译系统会自动先将他们转换成同一类型,然后再进行运算。

转换的基本规则是:“按数据长度增加的方向进行转换”,确保精度不变

2.所有的浮点运算都是以双精度进行

CPU在运算时有“字节对齐”的要求,这样的运算速度是最快的。

3.char型和short型数据参与运算时,必须先转换为int型 这也是涉及CPU的运行原理的。

4.有符号整型和无符号整型混合运算时,有符号型要转换成无符号型,运算的结果是无符号型的。

5.整型和浮点型混合运算时,整型先转换成浮点型,运算的结果是浮点型。

在这里插入图片描述

6.在赋值运算中,当赋值号两边的数据类型不同时,右边的类型会转换成左边的类型,然后再赋值到左边。如果右边的数据类型长度大于左边,这样他就会丢失数据精度。

写出下面的结果

#include <iostream>
using namespace std;

int main() {
    char str1[] = "abc";
    char str2[] = "abc";
    const char str3[] = "abc";
    const char str4[] = "abc";
    const char *str5 = "abc";
    const char *str6 = "abc";
    char *str7 = "abc";
    char *str8 = "abc";

    cout << (str1 == str2) << endl;  
    cout << (str3 == str4) << endl;  
    cout << (str5 == str6) << endl;  
    cout << (str7 == str8) << endl;  

    return 0;
}

答案:0 0 1 1;

​ str1,str2,str3,str4 是数组变量,它们有各自的内存空间;
​ 而 str5,str6,str7,str8 是指针,它们指向相同的常量区域。

sizeof如果用于数组,只能测出静态数组的大小,无法检测动态分配的或外部数组的大小。

使用异或操作

一个数和自身异或为0,异或操作的性质

自反性a ^ a = 0,任何数与自己异或结果为0。

交换性a ^ b = b ^ a,异或运算是可交换的。

结合性(a ^ b) ^ c = a ^ (b ^ c),异或运算是可结合的。

与零的结合a ^ 0 = a,任何数与0异或结果是它本身。

异或操作的应用

​ 1.交换两个数,通过三个异或操作交换’a’和’b’的值而不需要临时变量的参与

​ 2.找唯一出现的数:在一个数组中,所有数都出现两次,只有一个数出现一次,可以使用异或操作找到这个数

​ 3.取反:一个数与’0xFFFFFFFF’异或可以得到它的按位取反结果

对绝对地址0x100000赋值并且想让程序跳转到绝对地址0x100000去执行

(unsigned int*)0x100000 = 1234

跳转到绝对地址去执行

首先要将 0x100000 强制转换成函数指针,即:
(void (*)())0x100000
然后再调用它:
*((void (*)())0x100000)();
用 typedef 可以看得更直观些:
typedef void(*)() voidFuncPtr;
*((voidFuncPtr)0x100000)();

以下有一些代码和相关涉及的知识点,可以进行代码调试并进行相关知识点的补充和深入学习

翻转字符串的常用方式,

需要注意的一些知识点:末尾\0 ,strlen和sizeof的区别,malloc和free相关
#include <stdio.h>
#include <stdlib.h> 
#include <string.h>

#if 0 //使用循环遍历的方式将字符串数组的值进行翻转 
int main ()
{
	char *src = "hello,world";
	int len = strlen(src);
	char *des = (char*)malloc(len+1);//要为\0 分配一个空间
	char *d = des;
	char *s = &(src[len-1]); //指向最后一个字符
	while(len--)
	{
		*d++ = *s--;
	}
	*d = 0;//尾部要加\0
	printf("%s\n",des);
	free(des);// 使用完,应当释放空间,以免造成内存汇泄露
	return 0;
}
#else //使用 二分思想 遍历前一半元素,并和后一半元素进行交换 
int main ()
{
	char src[] = "hello,world";
	int len = strlen(src);
	for (int i=0; i<len; i++)
	{
		int t = src[i];
		src[i] = src[len-1-i];
		src[len-1-i] = t;
	}
	printf("%s\n",src);
	return 0;
}
#endif

Sizeof相关知识点

#include <iostream>
#include <string.h>
using namespace std;
void UpperCase(char str[])
{
	//这里传入的str的一个指针变量
	//sizeof()只能计算静态数组的大小 
	for (int i = 0; i < sizeof(str)/sizeof(str[0]); i++)
	if( 'a'<=str[i] && str[i]<='z' )
		str[i] -= ('a'-'A' );
	
	cout << sizeof(str)<<endl;//这里的str相当于是一个指针,如果在32位系统中,这里就是4个字节
							  //如果是在64位系统中,这里就是8个字节 
	
	cout << sizeof(str[0]) << endl;
}
int main ()
{
	char str[] = "aBcDe";
	cout << sizeof(str)<<endl;//这里传入的参数str就是数组,因为前面已经定义了一个数组 
	cout << sizeof(str[0]) << endl;
	cout << "str字符长度为:" << sizeof(str)/sizeof(str[0]) << endl;
	UpperCase( str );
	cout << str << endl;
	return 0;
}

数组和指针

数组的首地址、指针+1的值是如何计算的、
#include <stdio.h>

int main ()
{
	int a[5] = {1, 2, 3, 4, 5};
	int *ptr = (int *)(&a+1);	
	printf("%d,%d", *(a+1), *(ptr-1));	
	return 0;
} 
/***解释
	*(a+1)就是a[1]
	*(ptr-1)就是a[4]
	&a+1不是首地址+1,系统会认为加一个a数组的偏移,是偏移了一个数组的大小(本例是5个int)
	
	int *ptr = (int *)(&a + 1);
	则ptr实际上就是&(a[5]),也就是a+5
	&a 是数组指针,其类型为 int (*)[5];
	指针加1要根据指针类型加上一定的值,不同类型的指针+1之后大小不同 
	a 是长度为 5 的 int 数组指针,所以要加 5*sizeof(int)
	所以 ptr 实际是 a[5]
	a,&a 的地址是一样的,但意思不一样,a 是数组首地址,也就是 a[0]的地址,&a 是对象(数组)首地址,
	a+1 是数组下一元素的地址,即 a[1],&a+1 是下一个对象的地址,即 a[5]
****/
 

指针传递相关问题

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void getmemory(char *p)
{
	p=(char *) malloc(100);
	strcpy(p,"hello world");
}

int main( )
{
	char *str=NULL;
	getmemory(str);
	printf("%s/n",str);
	free(str);
	return 0;
}

/**这个代码是有问题的**/
/*****
*
指针传递问题,函数getmemory试图分配内存并将指针返回给调用者,但是由于指针是按值传递的,
getmemory中对指针`P`的修改不会影响`main`中的`str` 
*
解决办法应该是二级指针,也就是指向指针的指针。 

#include <stdio.h>
#include <stdlib.h>
#include <string.h> 
// 使用二级指针来传递指针
void getmemory(char **p) {
    *p = (char *)malloc(100);
    if (*p != NULL) { // 检查内存是否分配成功
        strcpy(*p, "hello world");
    }
}

int main() {
    char *str = NULL;
    getmemory(&str); // 传递指针的地址
    if (str != NULL) { // 检查内存是否分配成功
        printf("%s\n", str); // 正确的换行符
        free(str); // 释放内存
    }
    return 0;
}

*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h> 

void g(int**);

int main()
{
	int line[10],i;
	int *p=line; //p 是地址的地址
	for (i=0;i<10;i++)
	{
		*p=i;
		g(&p);//数组对应的值加 1
	}
	for(i=0;i<10;i++)
		printf("%d\n",line[i]);
	return 0;
}
void g(int**p)
{
	(**p)++;
	(*p)++;// 无效
}

结构体存储、处理器模式大小端、位域

#include <iostream>
#include <string.h>
#include <malloc.h>
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>

using namespace std;
typedef struct AA
{
	int b1:5;
	int b2:2;
}AA;
int main()
{
	AA aa;
	char cc[100];
	strcpy(cc,"0123456789abcdefghijklmnopqrstuvwxyz");
	memcpy(&aa,cc,sizeof(AA));
	cout << aa.b1 <<endl;
	cout << aa.b2 <<endl;
	return 0;
}

//00110011001100100011000100110000

结构体存储的字节对齐

#include <stdio.h>
#include <stdlib.h>

struct Apple{
	char b;
	double x;
};
int main()
{	
	Apple a;
	printf("%d\n",sizeof(a));
	return 0;
}

标签:知识点,常用,数组,int,易错,char,str,include,指针
From: https://blog.csdn.net/Stay_Hun_forward/article/details/140592528

相关文章

  • Makefile知识点总结(Linux下开发Risc-V单片机实例)
    Makefile会不会写makefile,从一个侧面决定一个人是否具备完成大型工程的能力。Makefile和make命令一起配合使用,为什么要使用makefile,原因以及优点在下文解释。简单辨析一下建立工程的三种方式Makefile使用非常广泛,通用性强,可跨平台但是语法比较严格,写一个通用,便于管理......
  • 常用端口申请IP SSL实现https教程
    在网络安全和数据保护方面,使用HTTPS协议来保护网站和应用程序的通信变得至关重要。一般情况下,HTTPS证书是基于域名签发的。但是,有时候我们可能需要为IP地址申请HTTPS证书,在一些特殊场景下确保通信的安全性。IPSSL证书也是SSL证书的一种,因其是颁发给IP地址使用的,所以我们......
  • Linux常用命令总结
    基础命令文件管理命令cata.txt#显示文本文件的内容cat-na.txt#显示文本文件的内容(并显示行号)cat-Aa.txt#显示文本文件的内容(含不可见字符)head/tail-na.txt#查看指定文件的头部/尾部内容less/more-na.txt#以分页方式查看长文件od-xa.txt#以十六进......
  • 音视频编解码常用知识点(转载)
    ##视频播放器原理视频播放器播放一个互联网上的视频文件,需要经过以下几个步骤:解协议,解封装,解码视音频,视音频同步。如果播放本地文件则不需要解协议,为以下几个步骤:解封装,解码视音频,视音频同步。他们的过程如图所示。*<strong>解协议的作用</strong>,就是将流媒体协议的数据,解析为......
  • Windows常用的cmd命令
    在Windows操作系统中,CMD(命令提示符)是一种用于执行命令行操作的工具。以下是一些常用的CMD命令:1.dir -显示当前目录下的文件和文件夹。2.cd -更改当前目录。3.cd.. -返回上一级目录。4.mkdir -创建新目录。5.rmdir -删除空目录。6.del -删除文件。7.d......
  • Qt基础 | Qt SQL模块介绍 | Qt SQL模块常用类及其常用函数介绍
    文章目录一、QtSQL模块概述1.Qtsql支持的数据库2.SQLite数据库3.QtSQL模块的主要类一、QtSQL模块概述  QtSQL模块提供数据库编程的支持,Qt支持多种常见的数据库,如MySQL、Oracle、MSSQLServer、SQLite等。QtSQL模块包括多个类,可以实现数据库连接、SQ......
  • Redisson常用的数据结构及应用场景
    Redisson提供了一系列高级数据结构,这些数据结构封装了Redis的原生数据类型,提供了JavaAPI的便利性和分布式特性。以下是Redisson中一些常用的数据结构,场景还在不断完善中:RBucket:这是一个简单的键值对存储,相当于Redis中的String类型。你可以使用它来存储和检索......
  • Docker常用命令
    容器打包成镜像#--pause=false表示不暂停容器打包#--pause=true或者默认表示暂停容器打包dockercommit-a"作者"-m"说明"--pause=false容器ID或容器名镜像名:版本打包到文件dockersave-o路径/文件名.tar镜像名:版本推荐几款学习编程的免费平台免费......
  • Docker简单使用、常用基础命令
    Docker简单使用、常用基础命令运行一个容器,输出helloworlddockerrunubuntu:laster/bin/echo"helloworld"docker:Docker的执行文件run:Docker的一个参数,作用是运行一个容器ubuntu:laster:指定要运行的镜像及版本,本地有直接运行,没有从镜像仓库下载运行/bin/echo"hello......
  • Django 表单常用字段参数
    DjangoForm表单,常用表单字段-CSDN博客        在Django中,表单(Form)是用来处理HTML表单数据的重要工具。Django的表单API允许你定义表单字段及其验证规则。每个表单字段都可以通过多种参数来定制其行为。以下是一些常用的表单字段参数:label:字段的标签,用于在HTML表单......