C language--null pointer of pointer (void *)


foreword

This article will introduce the headaches of the C language - pointers. Introduce the special pointer void *, introduce pointer memory allocation , how to use pointers as function parameters , and why you can't directly do some operations on void * pointers .


1. What is a void * pointer?

In the C language, there are several pointer types, the most special of which is the void * pointer. A void * pointer is a pointer to any type. The type it points to will not be determined during compilation, and it is pointed to in the program.

The void* pointer is simple to use

As mentioned above, the void * pointer can point to any type of pointer, so let's see how to use it.

int main() 
{
    
     
  
  int i_t[2]={
    
    0,1};
  
  void *v_t=i_t;/*void *可以获得指向任何地址,但不知道指向地址的类型大小*/
  /*普通指针使用,获得void *指针指向的地址并强制转换为int *类型,使之有知道指向类型的大小*/
  int * p_t=(int *)v_t;
  printf("Get int * i_t[0] = %d i_t[1] = %d \n",*p_t,*(p_t+1));

  /*void指针使用,可以直接给void *指针赋值,让它指向的类型改为int*的地址*/  
  v_t=p_t;

  printf("Get void * i_t[0] = %d i_t[1] = %d \n",*(int *)v_t,* (int *)(v_t+4));
  /*printf("Get void * i_t[0] = %d i_t[1] = %d \n",*(int *)v_t,* (char *)(v_t+4));*/ 
  //这两条printf输出效果一样,

  /*第二条printf输出警告,对void * 进行 +操作的警告,在GNU标准,对void *取加操作是仅仅类似char *的取加操作*/
  system("pause");
  return 0; 
}

operation result

Get int * i_t[0] = 0 i_t[1] = 1
Get void * i_t[0] = 0 i_t[1] = 1

Note that when void * is used, the + or - operation of the address of void * must be performed, and a forced type conversion is required to perform the operation. Not all compilers support the GNU standard.

Two, pointer memory size

pointer size

The pointer size is actually determined by the compiler and the operating system. The general rules are as follows.

  • 32-bit operating system, 32-bit compiler, pointer size is 32 bits
  • 64-bit operating system, 32-bit compiler, pointer size is 32 bits
  • 64-bit operating system, 64-bit compiler, pointer size is 64 bits

The 32-bit pointer size can address 4G, and the 62-bit pointer size can address 1800T addresses. This is just for understanding.

The difference between void * type pointers and other types of pointers

basic difference

The difference can be seen in the figure below. The memory where the pointer is located is the same, but the difference between the void * pointer and other pointers is that it contains the information of the pointed type. This feature means that it can point to any type of variable, and the void * type pointer just points to the address of this variable.
insert image description here

Value operation and address growth operation

Under the ANSI standard, if the void * type is not cast, it cannot perform value and address growth operations.

why? Let's first look at how pointers of type int * perform value-taking operations.

value operation

int * is the range of the obtained value (current first address + int type offset size). double * is the current first address + double type offset size. The offset size here is the number of bytes occupied, that is, sizeof (int) or sizeof (double).

The void * pointer does not contain the information of the type it points to, that is, without using mandatory type conversion, the compiler does not know how much the offset address range of the void * is the address range of the pointed value. That is to say, void * only has the first address currently pointing to the target, and does not know how many bytes after the first address are suitable. In this way, the compiler does not know whether it is 4 bytes or 8 bytes. It's not smart enough to tell this idiot, tell it with coercion.

address growth

Address growth and value are similar, void * I don’t know how many bytes to move by ++, so I won’t discuss it in detail. If you are smart, you must have the answer in your heart.

3. The void * pointer is passed as a function parameter

Here we will use the void * pointer as a parameter to make the program more versatile . Here we implement memset and memcpy in two C libraries , review the above knowledge points while implementing, and know why the incoming parameter is of void * type and why the length needs to be specified.

Implement memset

Function prototype: return the first address, and the input needs to specify the address to be initialized and the value and length to be initialized.

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

Function function: initialization function, which sets all the memory in a certain block to the specified value.

Take a look at the code and test function below:

void * my_memset(void *s,int c,size_t n)
{
    
    
    char *c_s=(char *)s;
    while(n--) *c_s++=c;
    return s;
}

int main() 
{
    
     
  
  int i_t[2]={
    
    1,1};

  my_memset(i_t,0,sizeof(i_t));

  for(int i=0;i<(sizeof(i_t)/sizeof(int));i++)
  printf(" %d ",i_t[i]);
  system("pause");
  return 0; 
}

operation result:

 0  0 

Implement memcpy

Function prototype:

void *memcpy(void *dest, void *src, unsigned int count);

Function function: copy count bytes from the memory area pointed to by src to the memory area pointed to by dest

Code and test function:

void * my_memcpy(void *dest,void *src,unsigned int size)
{
    
    
    char *b_dest=(char *)dest,*b_src=(char *)src;
    unsigned int len;
    for(len=size;len>0;len--)
    {
    
    
        *b_dest++=*b_src++;
    }
    return dest;
}


int main() 
{
    
     
  
  int i_t[2]={
    
    1,1};
  int i_f[2]={
    
    0,0};

  my_memcpy(i_f,i_t,sizeof(i_t));

  for(int i=0;i<(sizeof(i_t)/sizeof(int));i++)
  printf(" %d ",i_f[i]);
  system("pause");
  return 0; 
}

Simple analysis of the code idea:
the idea of ​​copying and clearing is to perform bit by bit operations on the incoming address.

As mentioned above, void * does not contain a type, so it can be passed in as any type, so that any type can be operated. What is passed in is just address information without a type. In C, since the pointer itself does not contain the information that needs to be operated on, the length information that needs to be operated must be passed in. Otherwise, the compiler will not know the scope of the operation just by passing in the pointer.

Summarize

Using the void * pointer can enhance the applicability of the function, but the operation of the void * pointer needs to be very careful, otherwise it is easy to cause the pointer operation to go out of bounds and cause some other unknown program errors.

Guess you like

Origin blog.csdn.net/weixin_46185705/article/details/123847127