推广 热搜: csgo  vue  angelababy  2023  gps  新车  htc  落地  app  p2p 

下的32位单目运算操作符,你了解多少?

   2023-06-05 网络整理佚名1550
核心提示:(str2)=6,字符串都是以\0结尾,所以所占字节数为6;根据求数组元素的个数也很简单,拿第一个来说,就是(str1)/(char)。「2、上面是求计算他们所占字节数,下面来看看怎么求字符串或数组的实际长度。第二个是将字符串常量的首地址赋值给p,对p操作就是对字符串常量进行修改!本质上是让指针变量str指向新内存的首地址,也就是把该首地址赋值给指针变量str。

inkMacSystemFont, "Segoe UI", Roboto, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Helvetica Neue", Helvetica, Arial, sans-serif;letter-spacing: 0.7px;">点击上方「inkMacSystemFont, "Segoe UI", Roboto, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Helvetica Neue", Helvetica, Arial, sans-serif;letter-spacing: 0.7px;color: rgb(61, 170, 214);">C语言中文社区inkMacSystemFont, "Segoe UI", Roboto, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "Helvetica Neue", Helvetica, Arial, sans-serif;letter-spacing: 0.7px;">」,选择「置顶公众号」第一时间查看C语言笔记!

来源:https://www.cnblogs.com/cyyz-le/p/11514477.html

上期回顾:

文本

“如无特殊说明,以下题目均为Linux下的32位C程序。”

让我们从一些简单的热身开始。

“1.计算以下值。”

char str1[] = {'a''b''c''d''e'};
char str2[] = "abcde";char *ptr = "abcde";char book[][80]={"计算机应用基础","C语言","C++程序设计","数据结构"};

(str1)=?

(str2)=?

指针)=?

(书)=?

(书[0])=?

“分析:”

(str1)=5,即5*(char)=5;

(str2)=6,字符串以'\0'结尾,所以占用字节数为6;

(ptr)=4,ptr在32位平台上是一个指针,大小为4字节;

(book)=320,book是一个二维数组,4801

(book[0])=80,book[0]是第一维数组,因为这个80*1

求数组元素个数也很简单。 取第一个为(str1)/(char)。

“2、上面是计算它们占用的字节数,下面看看如何求字符串或数组的实际长度,计算下面的值。”

char  arryA[] = {'a','b','c','\0','d','e'};
char  arryB[] = {'a','b','c','d','e'};
char  arryC[6] = {'a','b','c','d','e'};
char *str = "abcde";

“分析:”

(arryA) = 3,遇到'\0'就返回,不管后面有多少个字符;

(arryB) 无法确定长度,没有人为写'\0',会继续计算,直到找到终止符,结果未知;

(arryC)=5, 指定数组的大小,编译器会自动在空闲空间加上'\0',其实和char arryC[6] = {'a','b','c是一样的','d', 'e','\0'}; 相等的。

(str) = 5,不包括尾随的“\0”。

我们来看看上面两者的区别:

(1) 是C语言中的一元运算符,类似于++、--等;

对于数据类型,(type),例如(int)

对于变量,()

注意:不能用于函数类型、不完整类型或位域。 不完整类型是指存储大小未知的数据类型,例如存储大小未知的数组类型,

内容未知的结构或联合类型、void类型等。例如:(max),如果变量max定义为int max(); (), 然后

定义为 char[MAX],MAX 未知。

(2) 是一个原型为int(char *s)的函数;

的计算必须依赖字符序列中的'\0',用于判断字符序列是否结束。

《3.骗人的char str[]和char *str》

(1) 下面的操作是否合法? 如果出现问题,在什么阶段? 编译时还是运行时?

char str[] = "hello";
str[0] = 's';     //合法么

char *str = "hello";
p[0] = 's';      //合法么

“分析:”

这两个都编译成功,但第二个在运行时出现段错误。 我们来分析一下:

首先,“hello”是一个存放在静态数据区(data )中的字符串常量,它是在编译时确定的。 第一种是将一个字符串常量赋值给一个变量(全局变量在数据段,局部变量在栈区)。 其实就是将字符串常量复制到变量内存中,所以只修改了str[]变量。 价值。

二是将字符串常量的首地址赋值给p,对p的操作就是修改字符串常量! 因此出现了段错误。

(2) 理解了以上知识后,判断下列判断对错?

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;

“分析:”

结果是:0 0 1 1

先了解一下str1、str2、str3、str4,它们是什么? 它们是数组名,也就是数组第一个元素的地址! “str1 == str2”的本质是比较两个数组的地址是否相同。 正如我们上面所说,编译器为它们分配了新的存储空间来复制字符串“abc”。 这些变量在内存中是相互独立的,所以它们的地址一定是不同的!

然后了解str5、str6、str7、str8,它们是什么? 它们是指针,它们的值是字符串常量的地址! 它们都指向“abc”所在的静态数据区,所以都是相等的。

(3)更进一步:下面的程序是不是有问题? 如果有,问题出在哪里? 如何修改?

#include 
char *returnStr()
{
   char p[]="hello world!";
   return p;
}
int main()
{
   char *str = NULL;
   str = returnStr();
   printf("%s\n", str);
 
   return 0;
}

“分析:”

p是局部变量,复制字符串"hello word!"即可,局部变量存放在栈中,当函数退出时,栈被清空,p会被释放,所以返回的是释放的内存地址,这样做是错误的。

可以进行以下修改:

#include 
char *returnStr()
{
   char *p = "hello world!";
   return p;
}
int main()
{
   char *str = NULL;
   str = returnStr();
   printf("%s\n", str);
 
   return 0;
}

搜索公众号C语言中文社区,后台回复“资源”,免费领取200G编程资料。

这样写就不会有问题了,因为“hello world!” 存放在静态数据区,将该区的首地址赋值给指针p并返回,即使函数退出,字符串常量所在的内存也不会被回收,所以可以访问到字符串常量。

当然你也可以这样修改:

#include 
char *returnStr()
{
   static char p[] = "hello world!";
   return p;
}
int main()
{
   char *str = NULL;
   str = returnStr();
   printf("%s\n", str);
 
   return 0;
}

使用关键字,修改后的局部变量也会放在数据段中,即使函数退出,内存空间也不会被回收。

“4.数组作为函数参数传递”

我们经常使用数组作为函数的入参,我们来看看下面的函数有什么问题:

int func(int a[]){  
    int n = sizeof(a)/sizeof(int);  
    for(int i=0;i        printf("%d ",a[i]);  
        a[i]++;  
    }  
}  

原来n的值永远是1! 为什么会这样? 这是因为在C中,将数组传递给函数时,不能按值传递,而是会自动退化为指针。 下面三种写法其实是等价的:

“int func(int a[20]);” 等同于“int func(int a[]);” 等同于“int func(int *a);”。

《5.那些两个数互换的坑》

下面的代码想要交换两个数字。 有什么问题吗?

void swap(int* a, int* b)  
{  
    int *p;  
    p = a;  
    a = b;  
    b = p;  

“分析:”

当程序运行调用函数时,会将参数压入栈中,并为其分配新的空间。 这时候传入的其实是一个副本,如下图所示:

图像

a的值和b的值都是地址,交换a和b的值就是交换两个地址,也就是说只改变副本的地址,而指向的对象地址没变! .

正确的做法应该是这样的:

void swap(int* a, int* b)  
{  
    int tmp;  
    tmp = *a;  
    *a = *b;  
    *b = tmp;  

a和b虽然也是拷贝,但是通过函数内部的地址直接修改了对象的值,对应的实参也随之变化。

其实指针传递和值传递的本质都是值传递,值传递是传递一个待传递变量的副本。 复制后,实参地址和形参地址就没有联系了。 形参地址的修改不会影响实参,但是形参地址指向的对象的修改可以直接体现在实参上。 因为形参指向的对象就是实参的对象。 正因为如此,当我们传递一个指针作为参数时,我们需要使用const对其进行修饰,只是为了防止不小心修改了形参的地址。

《6.函数参数为指针时要小心》

下面的代码有什么问题? 运行结果会怎样?

void GetMem(char *p)
{
    p = (char*)malloc(100);   
}

void main()
{
    char *str = NULL;
    GetMem(str);
    strcpy(str, "hello word!");

    printf(str);
}

“分析:”

程序崩溃。 上面已经分析过,传递给函数形参的只是一个拷贝,修改形参p的地址对实参str没有影响。 所以str还是那个str,还是NULL。 这时候把字符串常量复制到一个空地址必然会导致程序崩溃。 下面的方法可以解决这个问题:

void GetMem(char **p)
{
    *p = (char*)malloc(100);   
}

void main()
{
    char *str = NULL;
    GetMem(&str);
    strcpy(str, "hello word!");
    printf(str);   free(str);   //不free会引起内存泄漏
}

看起来有点晦涩,其实很好理解。 本质上就是让指针变量str指向新内存的首地址,也就是把首地址赋值给指针变量str。 正如我们前面所说,指针传递本质上是值传递。 如果要在子函数中修改str的值,必须传递一个指向str的指针。 所以子函数需要传递str的地址,这样就可以通过指针修改str的值。 , 将内存首地址赋值给str。

《七、关于数组指针的疑惑》

(1) 说出下列词语的意思?

int *p1[10];
int (*p2)[10];

第一个是指针数组。 首先,它是一个数组,数组的元素都是指针。

第二个是数组指针,首先他是一个指针,它指向一个数组。

下面这张图可以很清楚的说明:

图像

(2)写出下面程序运行的结果

int a[5] = { 12345 };  
int *ptr = (int *)(&a + 1);  
printf("%d,%d", *(a + 1), *(ptr - 1)); 

“分析:”

答案是2,5。 这道题的关键是理解指针运算,“+1”就是偏移量的问题:类型T的指针以(T)为单位移动。

**a+1:** 基于数组首元素的地址,偏移一个(a[0])个单位。 因此,a+1代表数组的第一个元素,即2;

&a+1:在数组第一个元素的基础上,偏移一个(a)个单位,&a实际上是一个数组指针,类型为int()[5]。 因此&a+1实际上偏移了5个元素的长度,即a+5; 而ptr是int类型,所以“ptr-1”是负号(int*),也就是a[4]=5;

a为数组首地址,即a[0]的地址,a+1为数组下一个元素的地址,即a[1]; &a是对象的首地址,&a+1是下一个对象的地址,即a[5]。

《8.关于二级指针的问题》

给定语句 const char * const *pp; 以下哪项操作或说明是正确的?

(A) pp++ (B) (*pp)++ (C) (**pp)=\c\; (D) 以上都不正确

“分析:”

答案是A。

先从“一级指针”说起: (1) const char p:将变量p限制为只读。 这样,p=2等赋值操作就错了。 (2)const char p:p是一个char类型的指针,const只是限制p指向的对象是只读的。 这样,p=&a或p++等操作是合法的,但是p=4等操作是错误的,因为试图重写这个已经被限制为只读属性的对象。

(3) char const p:这个指针被限制为只读,所以p=&a或p++等操作都是非法的。 p=3 的操作是合法的,因为最终对象不限于只读。 (4) const char const p:两者都限于只读,不能改写。 再来看“二级指针”问题: (1)const char "p:p是一个指向指针的指针,const将其final对象限制为只读。显然,这个final对象也是char的变量类型。所以像 "p= 3 这样的赋值是错误的,而像 p=? 这样的赋值是错误的。 像 p++ 这样的操作是合法的。

(2)const char * const *p:将最终对象和p指向的指针限制为只读。 *p=? 的操作也是错误的,但是 p++ 是合法的。 (3) const char * const * const p: 都限制为只读,不能改写

“9, *p++, (*p)++, *++p, ++*p”

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

int *p = a;

*p++ 先取指针p指向的值(数组第一个元素1),再将指针p自增1

        cout << *p++;   // 结果为 1

        cout <<(*p++);  // 1

(*p)++ 先去指针p指向的值(数组第一个元素1),再将该值自增1(数组第一个元素变为2
        cout << (*p)++;  // 1
        cout <<((*p)++)  // 2
*++p   先将指针p自增1(此时指向数组第二个元素),* 操作再取出该值

        cout << *++p;  // 2
        cout <<(*++p)  // 2

++*p  先取指针p指向的值(数组第一个元素1),再将该值自增1(数组第一个元素变为2
       cout <<++*p;     // 2    
       cout <<(++*p)  // 2

《参考博客:》

1、《C语言与区别》:

2、“常量字符串为什么位于静态存储区?” “:

3.《值传递、指针传递、引用传递的区别》:

4.纽客网mlc回答:#

--- 结束---

 
反对 0举报 0 收藏 0 打赏 0评论 0
 
更多>同类资讯
推荐图文
推荐资讯
点击排行
网站首页  |  关于我们  |  联系方式  |  使用协议  |  版权隐私  |  网站地图  |  排名推广  |  广告服务  |  积分换礼  |  网站留言  |  RSS订阅  |  违规举报
Powered By DESTOON