linux c基础知识

一、常用基本函数

(一).memset函数

memset是一个初始化函数,作用是将某一块内存中的全部设置为指定的值。memset函数是按照字节对内存块进行初始化。

1.函数原型

void *memset(void *s, int c, size_t n);

  • s指向要填充的内存块。
  • c是要被设置的值。
  • n是要被设置该值的字符数。
  • 返回类型是一个指向存储区s的指针。

2.注意点

2.1 c的取值

  • memset只能取c的后八位赋给每个字节,因此c的高字节不会对初始化的结果产生影响,比如c取0(...00000000 00000000)与256(...00000001 00000000)结果一致。c通常使用0x00而不是0,这体现的是程序员对函数的理解
#include <stdio.h>
#include <string.h>
void main()
{
    int a[4];
    memset(a,0x00,sizeof(a));
    printf("a=%d\n",a[0]);
    memset(a,0xff,sizeof(a));
    printf("a=%d\n",a[0]);
    memset(a,256,sizeof(a));
    printf("a=%d\n",a[0]);  
}
结果为:
a=0
a=-1
a=0

2.2 初始化的范围

  • 对于数组或者结构体,只能初始化为0或-1,而不能是除此以外的数值,这是由于2.1特性导致的;
例:预期初始化数组为1。
#include <stdio.h>
#include <string.h>
void main()
{
    int a[4];
    memset(a,0x01,sizeof(a));
    printf("a=%d\n",a[0]);
}
结果为:
a=16843009(00000001 00000001 00000001 00000001)
  • 对于字符串,由于其本身一个元素只占一个字节,因此可以初始化为任意字符;
#include <stdio.h>
#include <string.h>

void main()
{
    char a[4+1];
    memset(a,'1',sizeof(a));
    printf("a=%c\n",a[0]);
    memset(a,'F',sizeof(a));
    printf("a=%c\n",a[0]);
}
结果为:
a=1
a=F

二、易错点

(一).printf函数

UE不能赋值粘贴时,试一下CTRL+0

printf函数实际是从右往左计算打印的。误以为从左开始时,会产生误解。
其实与具体编译器有关;使用GCC时是从右往左的

#include <string.h>
int main(void) 
{   
    
    int *ptr4,*ptr1;
    int urn[5] = { 100, 200, 300, 400, 500 }; 
    ptr1 = urn;
    ptr4 = urn;
    printf("urn=%p\n",urn);
    //元素地址
    printf("urn1=%p,urn2=%p,urn3=%p,ur4=%p,urn5=%p\n", ptr1, ptr1++,ptr1++,ptr1++,ptr1++); 
    //元素数值
    printf("urn1=%d,urn2=%d,urn3=%d,ur4=%d,urn5=%d\n", *ptr4, *ptr4++,*ptr4++,*ptr4++,*ptr4++); 
   
    return 0;      
}
结果为:
urn=0x7ffd5e134d90
urn1=0x7ffd5e134da0,urn2=0x7ffd5e134d9c,urn3=0x7ffd5e134d98,ur4=0x7ffd5e134d94,urn5=0x7ffd5e134d90
urn1=500,urn2=400,urn3=300,ur4=200,urn5=100

(二).指针形参

1.对指针形参直接赋值时,其实修改的是指针变量的值,并非对其指向的数据进行修改,只要对*指针变量赋值才是对原指针操作。
2.那字符串函数为什么可以使用memcpy(char *des,char *src,sizeof(src))?因为memcpy函数尽管传的是指针,其内部并非直接赋值(des=src,这样只会给让des指向src,只是改变了指针的内容,不会改变原字符串内容),而是*des++=*src++来赋值的。

#include <stdio.h> 

int copy_ptr(double *des,  double*src, int n); 
void printarr(double *arr,int n);

int main()
{
    
      int snResult = 0;
      int i = 0;
         double source[5] = {1.1, 2.2, 3.3, 4.4, 5.5}; 

        double target2[5]; 
        for ( i = 0;i<5;i++)
        {
            printf("source[%d]=%lf=%p\n",i,source[i],source+i);
        }
        printf("target2=%p\n",target2);

    
      printf("---------------------------\n");  
        copy_ptr(target2,source,5);
        printarr(target2,5);
        printf("---------------------------\n");    
}

int copy_ptr(double *des,  double*src, int n)
{
    int i = 0;
    
    des = src;//改变的是指针变量des的值,只是把他指向source数组地址,并非数组赋值
    return 0;
}

void printarr(double *arr,int n)
{
    int i = 0;
    for (i=0;i<n;i++)
    {
        printf("arr[%d]=%lf\n",i,arr[i]);
    }
    
}
打印结果,数组并未copy成功:
source[0]=1.100000=0x7fff01fa0970
source[1]=2.200000=0x7fff01fa0978
source[2]=3.300000=0x7fff01fa0980
source[3]=4.400000=0x7fff01fa0988
source[4]=5.500000=0x7fff01fa0990
target2=0x7fff01fa0940
---------------------------
arr[0]=0.000000
arr[1]=0.000000
arr[2]=0.000000
arr[3]=0.000000
arr[4]=0.000000
---------------------------

正确数组拷贝函数:

#include <stdio.h> 

int copy_ptr(double *des, const double*src, int n); 
void printarr(double *arr,int n);

int main()
{
    
      int snResult = 0;
      int i = 0;
        const double source[5] = {1.1, 2.2, 3.3, 4.4, 5.5}; 

        double target2[5]; 
        for ( i = 0;i<5;i++)
        {
            printf("source[%d]=%p\n",i,source+i);
        }
        printf("target2=%p\n",target2);

    
      printf("---------------------------\n");  
        copy_ptr(target2,source,5);
        printarr(target2,5);

        
}

int copy_ptr(double *des, const double*src, int n)
{
    int i = 0;
    
    #if 0
    des = src;//改变的是指针变量des的值
    for(i=0;i<n;i++)
    {
      printf("des==%f\n",des[i]);
  }
    #endif
    printf("des2=%d,%p,%p\n",sizeof(des),des,&des);
    

    for(i=0;i<n;i++)
    {
    *(des+i) = *(src+i);
  }
    
    return 0;
}

void printarr(double *arr,int n)
{
    int i = 0;
    for (i=0;i<n;i++)
    {
        printf("arr[%d]=%lf\n",i,arr[i]);
    }
    
}

(三).数组与指针

数组名是该数组首元素的地址,flizny == &flizny[0],是一个常量;所以flizny ++;flizny =arr;都不正确。
指针是是一个变量,存储的是地址;可以通过把数组名赋值给指针变量,来修改数组内容。

1.数组名不是变量 不能做左值

指针你可以把它看做一个整型(但不是)数据类型 它是一个变量 存储得是一个地址 比如你可以把一个地址赋值给它,这时指针变量就存储这个地址,再把另一个地址赋值给它, 这时指针变量就存另一个地址,它是可以变的,所以可以作为左值。

数组,你声明一个数组,系统就分配了一个内存单元 这时数组名表示得是这个数组首元素得地址,这个地址是不能变得,可以把它看做(但不是)常量,所以不能作为左值 。

2.sizeof(数组名)与sizeof(指向数组首地址的指针)
sizeof(数组名)是数组的实际大小,sizeof(指向数组首地址的指针)只是指针变量的大小,固定为8;
但是strlen(指针)可以使用,会显示指向字符串的长度。

#include <stdio.h> 
#include <string.h> 
int len( char *str);

void main()
{
    int snresult = 0;
    puts("input :");
    char str[20] = "AFDFAS1234";

    printf("%s,sizeof(str)=%d\n",str,sizeof(str));
    snresult = len(str); 
    
    printf("the main length is %d\n",snresult);
    
}

int len(char *str)
{
    int num=0;
    char *str1= str;
        
    printf("str=%s,str1=%s,%d,%d\n",str,str1,sizeof(str),sizeof(str1));
    while(*str1) 
    {   
        num++;      
        printf("the length is %d,%c\n",num,*str1);
        str1++;
    }
    
    return num; 
}
input :
AFDFAS1234,sizeof(str)=20
str=AFDFAS1234,str1=AFDFAS1234,8,8
the length is 1,A
the length is 2,F
the length is 3,D
the length is 4,F
the length is 5,A
the length is 6,S
the length is 7,1
the length is 8,2
the length is 9,3
the length is 10,4
the main length is 10

3.指针必须初始化,否则小心Segmentation fault

切记:创建一个指针 时,系统只分配了储存指针本身的内存,并未分配储存数据的内存。因此, 在使用指针之前,必须先用已分配的地址初始化它。

如果不初始化直接使用,系统会为这个变量自动分配一个地址,这个地址的值就是指针的值可能为0Xxxxxxxxx,不初始化这个指针的值的话,操作这个指针就想当于操作这个0Xxxxxxxxx这个地方的内容,就会出现问题,因为你并不知道这个指针指向地址的内容,可能为段系统代码,错改的话,可能会让系统崩溃。
运行时会报段错误:Segmentation fault
4.空指针不可访问,否则小心Segmentation fault
和不同的机器有关系。NULL指针,它作为一个特殊的指针变量,表示不指向任何东西。对其访问会造成Segmentation fault

int *node = NULL; 
*node = 1; //Segmentation fault

5.数组和指针

const char * pt1 = "Something is pointing at me.";
const char ar1[] = "Something is pointing at me.";

通常,字符串都作为可执行文件的一部分储存在数据段中。当把 程序载入内存时,也载入了程序中的字符串。字符串储存在静态存储区 (static memory)中。但是,程序在开始运行时才会为该数组分配内存。此时,才将字符串拷贝到数组中。注意,此时字符串有两个副本。一个是在静态内存中的字符串字面量,另一个是储存在ar1数 组中的字符串。
此后,编译器便把数组名ar1识别为该数组首元素地址(&ar1[0])的别 名。这里关键要理解,在数组形式中,ar1是地址常量。不能更改ar1,如果 改变了ar1,则意味着改变了数组的存储位置(即地址)。可以进行类似 ar1+1这样的操作,标识数组的下一个元素。但是不允许进行++ar1这样的操 作。递增运算符只能用于变量名前(或概括地说,只能用于可修改的左 值),不能用于常量。
字符串字面量被视为const数据。由于pt1指向这个const数据,所以应该 把pt1声明为指向const数据的指针。这意味着不能用pt1改变它所指向的数 据,但是仍然可以改变pt1的值(即,pt1指向的位置)。如果把一个字符串 字面量拷贝给一个数组,就可以随意改变数据,除非把数组声明为const。
初始化数组把静态存储区的字符串拷贝到数组中,而初始化指针 只把字符串的地址拷贝给指针。

(四).链表

1.构建链表(不含头指针,头指针即是首节点):


构建链表.png
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <stdlib.h>
1.链表能存储多个数据,是因为每次都会malloc一块内存;然后使用current和prv配合便可以将所有的内存块连接起来
2.需要三个指针:1个头指针,相当于导向牌,这里是链表的头;
3.current指针指向新开辟的内存块,为data赋值;干完活后把指针给prv
4.prv永远指向current的上一个指针,并为其和current的联结点建立关系,链表建立结束后prv即尾指针
#define null 0

struct node{
char data[100];
struct node* next;
};
void prt(struct node* plink)
{
    printf("-----------------------------\n");
    printf("NOW PRINTF:\n");
    while(plink != null)//若使用plink->next!= null,则不能打印出第5个数;使用plink,有在打印完第5个数后才为null
    {
        printf("the data is %s,%p\n",plink->data,plink->next);
        plink=plink->next;
    }

    
}

int main()
{   
    struct node* prv;
    struct node* current;
    struct node* head = NULL;//必须先初始化,否则比较if (null == head),报段错误
    
    int i = 0;
    char str[10] = "hello";

    for(i=0;i<5;i++)
    {
        current = (struct node*)malloc(sizeof(struct node));
        if(null == current)
        {
            printf("MALLOC ERROR \n");  
            return 0;
        }
        printf("addr is  %p\n",current);

        if (null == head)
        {
            head = current;

        }
        else
        {
            prv->next=current;//指针指向下一个节点
        }

        *(str+5) = i+1+'0';
        printf("%s\n",str);
        memcpy(current->data,str,7);
        current->next = null;//每次开辟后,current都做好结束的准备(尾指针)
        prv = current;//把值给prv,current准备指向下一个地址     

    }
    
    current = head;
    prt(current);
    

    while(current->next != null)
    {
        free(current);
        current=current->next;
        
    }   
}
结果:
addr is  0xdf2010
hello1
addr is  0xdf2090
hello2
addr is  0xdf2110
hello3
addr is  0xdf2190
hello4
addr is  0xdf2210
hello5
-----------------------------
NOW PRINTF:
the data is hello1,0xdf2090
the data is hello2,0xdf2110
the data is hello3,0xdf2190
the data is hello4,0xdf2210
the data is hello5,(nil)

2.构建链表(包含头指针):

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <stdlib.h>
//链表能存储多个数据,是因为每次都会malloc一块内存;然后使用current和prv配合便可以将所有的内存块连接起来

//需要三个指针:1个头指针,相当于导向牌,这里是链表的头;
//current指针指向新开辟的内存块,为data赋值;干完活后把指针给prv
//prv永远指向current的上一个指针,并为其和current的联结点建立关系

//链表建立结束后prv即尾指针
#define null 0

struct node{
char data[100];
struct node* next;
};

void countsum(struct node* plink)
{   
    int count = 0;
    
    while(plink != null)
    {
        count++;
        plink=plink->next;      
    }
    printf("count is %d \n",count); 
}

//第pos个节点后增加一个节点
int add(struct node* plink,int pos)
{
    int i = 0;
    struct node* pnew = (struct node*)malloc(sizeof(struct node));
    if(null == pnew )
    {
        printf("pnew malloc error!!!\n");
        return 1;
    }
    memset(pnew,0x00,sizeof(struct node));

    if(pos < 0 || pos >5)
    {
        printf("pos must between 1-5\n");  
        return 1;   
    }
    
    //pos为0,需要更换头指针指向
    if(0 == pos)
    {

        pnew->next = plink->next;//plink->next本来指向第一个节点
        plink->next = pnew;//现在让他指向新的pnew
        
        memcpy(pnew->data,"add-hello",10);
        
        printf("addr is  %p\n",pnew);
        printf("%s\n",pnew->data);
        return 0;
    }
    //定位到pos节点
    else
    {
            for(i=0;i<pos;i++)
            {
                plink = plink->next;    
            }
    }
    
    
    //在pos节点后增加,此时plink指向pos个节点
    memcpy(pnew->data,"add-hello",10);
    pnew->next = plink->next;//plink->next为pos+1个节点
    plink->next = pnew;//第pos节点指向pnew
    
        
    printf("addr is  %p\n",pnew);
    printf("%s\n",pnew->data);  
    return 0 ;
}

//删除第pos个节点
int del(struct node* plink,int pos)
{
    int i = 0;
    int result = 0;
        
    struct node* pre ;
    struct node* pbak ;
    

    //定位到第pos个节点
    for(i=0;i<pos;i++)
    {
        //pos前一个节点  
        pre = plink;
        plink=plink->next;      
    }
    pre->next = plink->next;
    
    //释放第pos节点内存
    free(plink);
    
    return 0;   
}


//打印链表
void prt(struct node* plink)
{
    printf("-----------------------------\n");
    printf("NOW PRINTF:\n");
    while(plink != null)//若使用plink->next!= null,则不能打印出第5个数;使用plink,有在打印完第5个数后才为null
    {
        printf("the data is %s,%p\n",plink->data,plink->next);
        plink=plink->next;
    }
}

int main()
{   
    struct node* prv = null;
    struct node* current;
    struct node* head = (struct node*)malloc(sizeof(struct node));
    if(null == head )
    {
        printf("head malloc error!!!\n");
        return 1;
    }
    
    memset(head,0x00,sizeof(struct node));
    int i = 0;
    int result = 0;
    int pos = 0;
    int count = 0;
    char str[10] = "hello";
    printf("head addr :%p\n\n\n",head);

    
 
    for(i=0;i<5;i++)
    {
        current = (struct node*)malloc(sizeof(struct node));
        if(null == current)
        {
            printf("current MALLOC ERROR \n");  
            return 1;
        }
        memset(current,0x00,sizeof(struct node));
        
        printf("addr is  %p\n",current);

        if (null == prv)//prv第一个节点刚创建时,还没有值;因此看用来作为头节点连接的标志
        {
            memcpy(head->data,"I'm head",10);
            head->next = current;
        }
        else
        {
            prv->next=current;//指针指向下一个节点
        }

        *(str+5) = i+1+'0';
        printf("%s\n",str);
        memcpy(current->data,str,7);
        current->next = null;//每次开辟后,current都做好结束的准备(尾指针)
        prv = current;//把值给prv,current准备指向下一个地址     
    }
    
    prt(head);
    countsum(head);

    //释放链表节点内存
    current = head;
    while(current != null)
    {
        free(current);
        current=current->next;      
    }   

}

3.链表反转:


image.png

image.png

image.png
//链表反转
//1.需要借助三个指针,分别指向1,2,3节点,依次向后推移;
//2.
void convert(struct node* plink)
{
    struct node *beg;
    struct node *mid;
    struct node *end;
      
  beg = plink->next;    
  mid =  beg->next;
  end = mid->next;
  //反转后首节点就是尾节点,需要指向null
  beg->next = NULL;
  
  while(end != NULL)
  {
   //让后一个节点指向前一个
    mid->next = beg;
    //三个节点向后推移
    beg = mid;
    mid = end;
    end =end->next;
  }
  
  //当end指向Null时,mid指向最后一个节点。然后完成最后一次指向。再把头节点重新定向
  mid->next = beg;
  plink->next = mid;

}

6.C语言关键字作用
6.1 static关键字
【C语言】关键字static详解_static关键字-CSDN博客
6.1.1
static 作为储存类说明符修饰局部变量,局部变量作用域不改变,但生命周期变为整个程序执行期间。
静态局部变量在程序执行声明该变量时被首次初始化,以后的函数调用不再进行初始化,也有人称作静态局部变量只进行一次初始化。
6.1.2
static 修饰全局变量或者函数,会将作用域被限制在本文件,变量只可在本文件中被访问,其他文件可定义同名变量而不会引起冲突。
6.1.3
(1)c语言可以返回局部变量的数值,但不能返回局部变量的地址,因为局部变量保存在栈上,调用后即被销毁,返回解引用时可能存在错误;
可以通过定义静态局部变量来解决,因为静态变量作用于整个生命周期;

示例.png

(2)对于c语言函数可以返回指针,但要求指向的地址必须有意义,即不能是栈上的;


image.png
常量保存于数据段,因此可以通过.png

image.png

此时指针指向栈上地址,虽然编译不报错,但结果有问题.png
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,752评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,100评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,244评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,099评论 1 286
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,210评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,307评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,346评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,133评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,546评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,849评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,019评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,702评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,331评论 3 319
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,030评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,260评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,871评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,898评论 2 351

推荐阅读更多精彩内容