C重点知识点总结(2)

  1. 指针和数组名都表示地址区别?
    对于指针,编译器是先取出指针所在地址里存的地址值,然后再这个地址去找这个内存里的值。而数组名不一样,数组名的自身地址开始算数组的开始地址,而并不是数组名的地址里存的值是数组的开始地址。
    对应数组名可以理解为一个静态的地址,而指针则是一个动态的变化的地址。
    例子:
    在another.c中
    char *p = “hello world!”;
    现在test.c要调用another.c中的p指针,是这样声明的:extern p[];
    如何读出hello world?
    printf(“%s \n”, ((char*)((unsigned int)p)) );
    解释如下:p是指针,所以占4个字节,4个字节的内容指向hello world这个字符串。但是由于我们在test.c文件中是将p声明成了数组,如果直接用prinf(“%s \n”, p); 那么结果是乱码,原因就是他会将p当前的地址当作这个数组的开始,然后直接将这四个字节里的每个值当作字符输出,所以当然会错误, 所以我们首先应该将p这四个字节整体的值取出来,开始想的是直接(unsigned int)p 这样强制转换,但后来发现 这样肯定不可以,因为p的值是这个四字节数组的开始地址值,(unsigned int)p 是将地址值转成了unsigned int 型, 所以应该是先将(unsigned int*)p 这样p就指向了这块四字节内存的指针了,然后提取出来,就可以了 就是图上边的printf这条语句;

    2、 数组的赋值

方法一:
for( i=0; i<5; i++)
{
   a[i] = i;
} 
方法二:
int *p = a;
For( i=0;  i<5;  i++)
{
   *p++ = i;
}

这两种方法虽然作用都一样,但是方法二的工作效率要比方法一高很多。

3、参数可变函数

参数可变函数的实现要依赖于 stdarg.h 这个头文件

#include <stdio.h>
#include <stdarg.h>
float average(int n, ...)
{
    va_list args;   // 定义可变参数列表
    int i = 0;
    float sum = 0;
    va_start(args, n);   //开始读取可变参数列表
    for(i=0; i<n; i++)
    {
        sum += va_arg(args, int);  // 读取参数值  注意要注明取的类型;
    }
    va_end(args);   //  读取完毕 
    return sum ;
}

int main()
{
    printf("%f\n", average(5, 1, 2, 3, 4, 5));
    printf("%f\n", average(4, 1, 2, 3, 4));
    return 0;
}

对参数可变函数的说明:
  参数列表中至少要存在一个确定的命名参数
  无法确定实际的参数个数
  从头到尾进行顺序访问
4. 内存管理(堆栈)
对于局部变量,都是保存在栈中,而动态申请的内存是从堆中申请的。只读存储区的数据不能修改
例如:char *s = “hello world!”; 这里的s就是指向只读存储区,不能修改hello world 这句话里的内容;
对于栈:是用来维护函数调用,没有栈,就不会有局部变量,当函数调用结束后,栈就自然会销毁;
对于堆来说,堆是为malloc 这些申请动态内存准备的,如果在函数中申请完后,函数结束,申请的内存并不会被销毁,这个是跟栈不同的。
系统对于堆的管理方式: 空闲链表法、位图法、对象池法等等;后面会有对内存管理的说明博文请继续关注
5. 复合字(C99)
以前定义数组的方法是:
int a[3] = {1,2,3};
但现在可以(int [3]){1,2,3};
但是因为现在这种定义没有数组名,所以没法直接引用, 所以使用的方法只有两种:
第一种: int *p = (int [3]) {1,2 ,3};
第二种作为函数参数: 声明一个函数:void fun(int a[], int n);
在调用的时候:fun( (int [3]){1,2,3}, 3 );
定义二维数组也可以使用复合字:
(int [2][3]){ {1,2,3}, {4,5,6} };

6、 对几个常用易错的函数进行说明
strcpy函数:
如果被复制的数组的空间大于要复制过去的数组空间,程序就会出错;可以使用strncpy(char *des, char *src, size_t num);
num表示从源中复制多少到目标里去;
注意:

int main()
{
char s[] = "1234";
char d[] = "654321";
strncpy(d, s, 4);
printf("%s\n", d);
return 0;
}

这里的num如果小于s的长度5的话,由于stncpy并不会把结束符复制进去,所以会出现输出的时候输出的是123421这种情况;所以最后等于源的长度否则会出问题;因为strcpy就是简单地内存copy效率很高!

#include "stdio.h"
#include "string.h"
int main()
{
char s[] = "1234567890";
char d[] = "654321";
printf("sizeof(d)=%d\n", sizeof(d));
printf("s=%s\n", s);
printf("d=%s\n", d);
strncpy(d, s, 13);
printf("sizeof(d)=%d\n", sizeof(d));
printf("s=%s\n", s);
printf("d=%s\n", d);
return 0;
}

这里的最后一个s输出的是890,而不是1234567890,为什么呢?因为栈是从高地址向低地址生长的,这里有个情况说一下:首先是strncpy这里有个13,这个是大于s的长度11的,这里strncpy 会看\0和13那个先到,哪个先到就停止复制了。然后就是因为s的数组长度已经大于d的长度了,所以会将s数组后面的内容进行了覆盖,结果导致s的内容发生了覆盖,把1234覆盖了,变成了890 \0了,可以将s和d的首地址输出一下就明白了
然后就是sizeof的问题了 为什么两次输出的都是7呢?? 明明后来的d的\0出现在了第11位上了,这是因为sizeof 是在编译的时候就已经算出答案了,那个时候程序还没有运行,所以d的数组并没有发生变化。
结论:所以最好把长度设置成源的长度,避免出现你不想要的现象。
printf和scanf

printf(); 这个函数是有返回值的,当输出错误的时候会返回负数。
printf(“123”“456\n”); 这里是两个字符串变成一个字符串,然后输出,但是一定要注意,两个字符串之间要是有逗号的话,就只能输出第一个字符串了,不会把他们合并了。

char b[] = "ssdfg""hell world";
printf("%s\n", b);

这里也会把两个字符串合并成一个字符串

scanf() 函数, 他的返回类型如果成功读入,那么返回读入的数,如果读的是文件 读到结尾,那么返回EOF;

fgets(), 是读取文件的函数,也可以读取键盘:
第二个参数表示要读入的字符数,第三个是要读哪个文件,当要从键盘读取的时候,可以使用stdin,就表示要从键盘中读取;如果还没有读够这么多的字符,但是读到了换行符,也会停止,但是他会把换行符存到字符串中。
puts()和printf()的区别在于puts() 函数会自动加上换行符
关于字符串的处理函数:
strcat(): 两个字符串参数, 将第二个字符串拷贝到第一个字符串的结尾,但是并不会检查第一个数组是否可以容纳第二个字符串。
strncat(): 第三个参数指明最多允许添加的字符数;但是如果碰到了结束符,那么提前结束;
strcpy() : 返回的是被复制的字符串
第一个参数可以不是从数组的头开始的;
例如:

char *org = "beast 123";
char copy[] = "be the best that you can be";
char *ps;
ps = strcpy(copy+7, org);
puts(copy);
puts(ps);
*ps = 'A';
puts(copy);
puts(ps);
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值