c语言要点摘录(21~24 数组与指针-上)

二十一.指针基础

             指针本质:指针本质上也是一个变量,需要占用一定的内存空间,指针用于保存内存地址的值。

       传值调用与传址调用:1.指针是变量,因此可以声明指针参数

                                               2.在一个函数体内部需要改变实参的值,则需要使用指针参数,传址。

                                               3.函数调用时,实参值将复制到形参

                                               4.指针适用于复杂类型作为参数的函数中(减少开销)  

          注:在c++中,在函数参数可以直接使用传址调用(foo(&a,&b)),不用指针;c中要实现传址调用,只有使用指针        (foo(*a,*b))。

        const与指针:

             


二十二.数组基础

            数组地址与数组名

                  1.数组名代表数组首元素的地址

                  2.数组的地址需要用取地址符&才能得到

                  3.数组首元素的地址值与数组的地址值相同

                  4.数组首元素的地址与数组的地址是两个不同的概念            

//例如:a[5],数组名是a,数组元素首地址&a[0],数组地址是&a


            数组名:1.数组名可以看做一个常量指针

                             2.数组名“指向“的是内存中数组首元素的起始位置

                             3.数组名只能作为右值使用

                  只有以下场合数组名不能看做常量指针:

                              1.数组名作为sizeof操作符参数

int a[5];
sizeof(a) = 20;
sizeof(a+0)=4;
sizeof(&a)=4;
sizeof(a+1)=4;
sizeof(*a) = 4;

只有当数组名作为sizeof参数的时候才是对整个数组求占用内存长度。

                              2.数组名作为&运算符的参数        ,&a=a=&(a+0);

            常见错误:定义为指针,声明为数组

#include <stdio.h>

// another file
// char* p = "Hello World!";

extern char p[];

int main()
{
    printf("%s\n", p);
    
    return 0;
}

原因是:编译器处理指针是一次寻址,要通过p来寻址,p的地址和字符串地址分离,

                处理数组无寻址,数组名p的地址就代表数组地址

修正版:

#include <stdio.h>

// another file
// char* p = "Hello World!";

extern char p[];

int main()
{
    printf("%s\n", (char*)(*((unsigned int*)p)));
    
    return 0;
}

在这个文件中p被误认为数组,强制类型转化为unsigned int类型的指针(unsigned int*)p,然后将p所代表的地址取出*((unsigned int*)p),然后将它作为指针输出

(char*)(*((unsigned int*)p)),结果就是正确的。


     数组小结: 1.数组是一片连续的内存空间

                            2.数组的地址和数组首元素的地址意义不同

                            3.数组名在大多数情况下被当成常量指针处理

                            4.数组名其实并不是指针,在外部声明时不能混淆


二十三.数组与指针分析

                指针是一种特殊变量,与整数的运算规则为:

                           p + n;<---------->(unsigned int)p+n*sizeof(*p);

                注意:当指针指向一个同类型的数组元素时,p+1将指向当前元素的下一个元素;p-1将指向当前元素的上一个元素。


               指针之间只支持减法运算,且必须参与运算的指针类型必须相同。                     

                           p1 - p2;<-------->((unsigned int)p1  -   (unsigned int)p2)/sizeof(type);           

               注意:1.只有当两个指针指向同一个数组中的元素时,指针相减才有意义,其意义为指针所指元素的下标差

                            2.当两个指针所指向的元素不在同一个数组中时,结果未定义(但是可以计算,前提是类型相同)

              

               指针的比较:可以进行关系运算<,   <=,    >,    >=    

               注意:1.指针关系运算的前提是同时指向同一个数组中的元素

                          2.任意两个指针之间的比较运算(==  !=)无限制(可以不是同一个数组,因为只是比较地址是否相等)



              指针形式和下表形式访问数组: 理论上指针形式效率更高,现代编译器代码优化率已经优化,在固定增量时,效率已经相当。


        a和&a的区别:

             a为数组是数组首元素的地址

             &a为整个数组的地址

             a和&a的意义不同其区别在于指针运算

             a+1--->(unsigned int)a + sizeof(*a)

             &a+1---->(unsigned int)(&a) + sizeof(*&a)=(unsigend int)(&a) + sizeof(a)

求数组长度:length = sizeof(a)/sizeof(a[0])

     

#include <stdio.h>

int main()
{
    int a[5] = {1, 2, 3, 4, 5};
    int* p1 = (int*)(&a + 1); 
    int* p2 = (int*)((int)a + 1);
    int* p3 = (int*)(a + 1);
    
    printf("%d, %d, %d\n", p1[-1], p2[0], p3[1]);
    
    return 0;
}
输出:p1[-1]<===> a[4]=5   p2[0]<===>0x02000000(小端)   p3[1]<===>a[2]=3
          

     数组参数:c语言中,数组作为函数参数时,编译器将其编译成对应的指针

                                    void  f(int a[]); <===>void f(int* a);

                                    void f(int a[5]);<===>void f(int* a);

                      结论:一般情况下,当定义的函数中有数组参数时,需要定义另一个参数来标示数组的大小   

  

   指针和数组的对比:


     1.数组声明时编译器自动分配一片连续内存空间

     2.指针声明时只分配了用于容纳指针的4字节空间

     3.在作为函数参数时,数组参数和指针参数等价

     4.数组名在多数情况下可以看做常量指针,其值不能改变

     5.指针的本质是变量,保存的值被看做内存中的地址



二十四.c语言中的字符串

           从概念上讲,c语言中没有字符串数据类型,使用字符数组来模拟字符串,c语言中的字符串是以'\0'结束的字符数组

            c语言中的字符串可以分配于栈空间,堆空间或者只读存储区

        

#include <stdio.h>
#include <malloc.h>

int main()
{
    char s1[] = {'H', 'e', 'l', 'l', 'o'};//不是字符串
    char s2[] = {'H', 'e', 'l', 'l', 'o', '\0'};\\\栈中的字符串
    char* s3 = "Hello";\\只读存储区
    char* s4 = (char*)malloc(6*sizeof(char));\\堆中的字符串
	
    s4[0] = 'H';
    s4[1] = 'e';
    s4[2] = 'l';
    s4[3] = 'l';
    s4[4] = 'o';
    s4[5] = '\0';
	
    free(s4);
	
    return 0;
}

    字符串的长度:指的是字符串所包含字符的个数,第一个'\0'字符前出现的字符个数,c语言中通过'\0'结束符来确定字符串长度。

                strlen库函数就是基于这个标准来计算字符串长度。

     

#include<stdio.h>
#include<string.h>

char g[100];

int main()
{
    g[0] = 'H';
    g[1] = 'e';
    g[2] = 'l';
    g[3] = 'l';
    g[4] = 'o';
    g[5] = '\0';
    g[6] = 'H';
    g[7] = 'e';
    g[8] = 'l';
    g[9] = 'l';
    g[10] = 'o';
	
	printf("length = %d\n",strlen(g));
	printf("size = %d\n",sizeof(g));
	return 0;

}
输出为:length = 5,size = 100;这就是字符串和字符数组的区别。
         

关于strlen的警告:strlen的返回值是无符号数定义的,因此详见不可能产生负数(总是大于0),下面的语句不等价:

        

char* a = "123";
char* b = "1234";

if(strlen(a) >= strlen(b))
{
  ……
}

if(strlen(a) - strlen(b) >= 0)
{
  ……
}

一句话实现strlen:

#include <stdio.h>
#include <assert.h>

size_t strlen(const char* s)
{
    return ( assert(s), (*s ? (strlen(s+1) + 1) : 0) );
}

int main()
{
    printf("%d\n", strlen( NULL));
    
    return 0;
}

不受限制的字符串函数:通过寻找字符串的结束符'\0',来判断长度

字符串复制:char*  strcpy(char* dst,  const char* src);

字符串连接:char*  strcat(char* dst,const char* src);

字符串比较:int    strcmp(const char* s1,const  char* s2);   

注意:输入参数中必须包含'\0'。

          strcpy和strcat必须保证目标字符数组的剩余空间足够保存整个源字符串

          strcmp以0表示两个字符串相等,第一个大于第二个返回大于0,第一个小于第二个返回小于0

          strcmp不会修改参数值 ,检测'\0'为结束符

实现strcpy函数:

#include <stdio.h>
#include <assert.h>

char* strcpy(char* dst, const char* src)
{
    char* ret = dst;
    
    assert(dst && src);//判断是否为空
    
    while( (*dst++ = *src++) != '\0' );
    
    return ret;
}

int main()
{
    char dst[20];
    
    printf("%s\n", strcpy(dst, "Delphi Tang!"));
    
    return 0;
}

长度受限的字符串函数:

     字符串复制:char* strncpy(char* dst,const char* src,size_t len);

     字符串连接:char* strncat(char* dst,const char* src,size_t len);

     字符串比较:char* strncmp(const char* s1,const char* s2,size_t len);

注意事项:strncpy:只复制len个字符到目标字符串

                               1.源字符串的长度小于len时,剩余空间以'\0'填充。

                               2.当源字符串的长度大于len时,只有len个字符串会被复制,且它将不会以'\0'结束。

                 strncat:最多从源字符串中复制len个字符到目标字符串中

                                1.总是在结果字符串后面添加 ‘\0’

                                2.不会用'\0'填充目标串中的剩余空间

                  strncmp:只比较len个字符是否相等







  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值