1. 程式人生 > >【C++ -> 容器初始化】結構體巢狀複雜結構初始化

【C++ -> 容器初始化】結構體巢狀複雜結構初始化

案例簡述

最近在設計資料結構時,資料之間較為複雜,資料結構如下:

typedef struct tagDataInfo
{
    long offs;                // 索引
    char name[20];            // 姓名
    std::list<int> ord_list;  // 定單索引列表
    std::map<std::string, std::string> str_map; // 編號對應管理

    tagDataInfo() { memset(this, 0, sizeof(*this)); };
} DataInfo;

程式執行的時候,程式core dump了,經過查閱資料以及gdb除錯,發現使用memset並沒有像預期那樣把所有元素都初始化。

知識梳理

結構很簡單,就是為了體現資料之間的對應關係。
老生常談的函式介紹:

函式原型
void *memset(void *s, int ch, size_t n);

函式功能

將s所指向的某一塊記憶體中的前n個位元組的內容全部設定為ch指定的ASCII值,
第一個值為指定的記憶體地址,塊的大小由第三個引數指定,這個函式通常為新申請的記憶體做初始化工作,其返回值為指向s的指標,它是對較大的結構體或陣列進行清零操作的一種最快方法。

引入標頭檔案

<memory.h><string.h>

應用場景

memset函式通常用來對一塊已經分配地址的記憶體進行初始化,並且通常初始化為0或者字元’\0’。如上述例子!

注意事項

(1)memset中的第三個引數一定要使用sizeof操作符,因為每個系統下對型別長度的定義可能不一樣。
(2)memset中的第一個引數一定要是一個已知的、已經被分配記憶體的地址,否則會出錯。
(3)大家可能比較疑惑,memset的第一個引數已經有了被初始化空間的首地址,為什麼還要返回一個void*的指標去指向這個地址呢?這裡之所以還要返回這個指標是為了實現鏈式程式設計,所謂鏈式程式設計,舉個例子大家就明白了。
(4)最後一點,也是最重要的一點。一定要注意,memset是按照位元組對待初始化空間進行初始化的,也就是說,函式裡面的第二個引數的那個初值(一般為0)是按照一個一個位元組往第一個引數所指區域賦值的,所以,對於單位元組資料型別(char)可以初始化為任意支援的值,都沒有問題,但是對於非多位元組資料型別只能初始化為0,而不能初始化成別的初值,因為對所有位元組按任意順序賦值0的結果都是0,而如果初始化為其他的值,就會一個位元組一個位元組的進行賦值,從而出現奇怪的結果。比如說,上面的例3之所以沒有出錯就是因為初始化為0,但是如果初始化為1,那麼因為int一般是4個位元組,那麼相當於將一個int元素初始化成了0000 0001 0000 0001 0000 0001 0000 0001,這樣對於一個int元素肯定不是1,而是一個很大的數,結果出乎意料,所以一定要記住這一點,非常重要!!!

引申:何謂鏈式程式設計?
舉例:

#include <iostream>
#include "string.h"
#include "stdlib.h"
#include "stdio.h"

int main()
{
        // 鏈式程式設計
        int i = 0;
        char cBuf [10];
        char cBuf1[10];

        // 這裡是關鍵!!!
        memcpy(cBuf1, memset(cBuf, 'a', sizeof(char) * 10), sizeof(char) * 10);

        for (i = 0; i < 10; ++i)
        {
                printf("%c", cBuf[i]);
        }
        printf("\n");

        for (i = 0; i < 10; ++i)
        {
                printf("%c", cBuf1[i]);
        }
                printf("\n");

        return 0;
}

從上面這個例子中就可以看出,在memcpy這個函式中,直接使用了memset的返回值,用其來拷貝cBuf1這個字元陣列,這樣就可以直接連起來寫,看起來十分方便。但是這個例子的應用形式卻很少,只是為了說明這個問題才這樣寫的,這種結構在很多函式庫裡面比較常見,比如字串操作函式等,但是鏈式程式設計也使得程式碼變得有些不直觀,所以要有所取捨。

問題剖析

1)上述也說了,memset是以位元組為單位,初始化記憶體塊
2)當結構體中包含指標時,在使用memset初始化要特別注意:

#include <iostream>
#include "string.h"
#include "stdlib.h"
#include "stdio.h"

int main()
{
        struct Parameters {
                int x;
                int* p_x;
        };
        Parameters par;
        par.p_x = new int[10];
        memset(&par, 0, sizeof(par));
        // 輸出nil
        printf("地址: %p \n", par.p_x);

        int* ptr = new int[10];
        // 正確輸出
        printf("地址: %p \n", ptr);
        return 0;
}

當memset初始化時,並不會初始化p_x指向的int陣列單元的值,而會把已經分配過記憶體的p_x指標本身設定為0,造成記憶體洩漏。

同理,對std::vector等資料型別,顯而易見也是不應該使用memset來初始化的。

正確Init

typedef struct tagDataInfo
{
    long offs;                // 索引
    char name[20];            // 姓名
    std::list<int> ord_list;  // 定單索引列表
    std::map<std::string, std::string> str_map; // 編號對應管理

    tagDataInfo() : offs(0)
    {
        memset(name, '0', sizeof(char) * 20);
        ord_list.clear();
        str_map.clear();
    };
} DataInfo;

相關推薦

C++ -> 容器初始結構複雜結構初始

案例簡述 最近在設計資料結構時,資料之間較為複雜,資料結構如下: typedef struct tagDataInfo { long offs; // 索引 char name[20]; /

第22節 C語言結構結構結構指標與結構陣列的程式碼實現

結構體 #include <stdio.h> //第一步 struct Student { //學號 int no; //姓名 char name[20]; //性別 char

C語言結構的前向宣告,以及結構const結構成員的問題

如果你在宣告一個結構體A時,要使用一個未宣告的結構體B時,該怎麼辦?如下: #include <stdio.h> typedef struct demo{     struct stu test;     const int test2;     int te

結構結構指標

struct和typedef struct 首先是結構體的定義格式分三塊來講述:  1 首先://注意在C和C++裡不同    在C中定義一個結構體型別要用typedef:    typedef struct Student    {    int a;   

day07 結構 匿名結構 結構初始 訪問 結構 結構的儲存空間 結構陣列

//結構體的定義/*  struct 結構體的定義{  資料型別成員變數1;  資料型別成員變數2;  .....  資料型別成員變數n;  }; 注意“;”  *///typedef(第一種方式)//先定義結構體,在說明結構體變數//struct myPoint{//    float x;//行座標// 

C++ 結構

該部落格主要是示例如何使用結構體巢狀進行函式變數的封裝傳遞。 主要任務是使用一個結構體包含一些目標的名稱、距離、角度資訊。不同場景下的目標數量是不一樣的,所以不能使用固定的陣列來進行儲存,使用陣列太大時浪費空間,太小是又擔心不能正確儲存資訊。所以建議使用結構體巢

c語言結構的對齊方式

1:在程式碼中有結構體巢狀的方式,又需要獲取每個變數的地址。那麼需要了解結構體巢狀和單一結構體的一些對齊方面的規則。2:對於嵌入式的裝置來說,對齊很重要,有些cpu不支援不對齊的訪問方式,有的cpu支援,只是效率上會降低。3:arm中預設編譯的時候會進行對齊。規則一:結構體中

C語言_結構和字串陣列的表示_plusC14.3

#include<stdio.h> #define LEN 20 const char *mgs[5]= {"qqqq","wwww","EEEEEEE","RRRR","TTTTT" }; struct apple {char smell[LEN];char

C語言之結構一級指標和二級指標

指標是C語言的精華,也是c中最難的部分;為了鍛鍊和提高自己對指標的理解,特在結構體中巢狀一級指標和二級指標來加深對。 #include<stdio.h> #include <string.h> #include<stdlib.h&

C語言-結構指標及結構

C語言中結構體是一種構造型別,和陣列、基本資料型別一樣,可以定義指向該種類型的指標。結構體指標的定義類似其他基本資料型別的定義,格式如下 struct 結構體名 * 指標名; 比如: struct person{char[20] name; int age;};//先定義一

jchdl - RTL例項 - And2And(結構的使用)

https://mp.weixin.qq.com/s/PQIPkDymvcGc_re8ux50vA   結構體可以巢狀使用。   參考連結 https://github.com/wjcdx/jchdl/blob/master/src/org/jchdl

1263 STL map和結構使用。

例題:HDU - 1263 夏天來了~~好開心啊,呵呵,好多好多水果~~ Joe經營著一個不大的水果店.他認為生存之道就是經營最受顧客歡迎的水果.現在他想要一份水果銷售情況的明細表,這樣Joe就可以很容易掌握所有水果的銷售情況了. Input 第一行正整數N(0<

NUC140 之共用結構

 共用體和結構體巢狀的情況最早見於NUC1XXX的庫函式,後來用的非常順手, 看下面的程式碼: typedef union { //定義一種共用體,它包含兩個成員,一個是8位的總的錯誤訊號(錯誤暫存器位元組1所用) uint8_t error_sampl

go結構的切片陣列

package main import ( "fmt" ) type XCDataStu struct { Id int `json:"id" xorm:"id"` Name string `json:"name" xorm:"name"` } ty

結構時的sizeof運算

如果結果體中的成員包括陣列或者其他結構體,在資料對齊時,要以結構體中最深層的基本資料型別為準。所謂結構體中最深層的基本資料型別是指:如果結構體中的成員為複雜資料型別,不應以複雜資料型別所佔空間作為資料對齊的標準,而應深入複雜資料型別內部,檢視其所包含的基本資料型別所佔空間。

go語言基礎 結構

go語言當中的聚合和繼承都是模擬出來的,子類是可以使用父類裡的欄位或功能結構體的巢狀type A struct{  //父類name stringage int}type B struct{  //子類a A // 模擬聚合關係.....}type C struct{   /

結構二級指標

#include <stdio.h> #include <stdlib.h> #include <

C語言經典例項-指向結構的指標變數

本例項通過結構體指標變數實現在窗體上顯示學生資訊,執行程式後,將學生資訊輸出在窗體上,效果如圖所示: 在本例項中,我們先宣告一個結構體變數,並定義一個指標變數指向struct student型別的資料,程式碼如下: #include<stdio.h> #inc

C語言結構的位元組對齊例項C語言筆試題

 一、筆試題目:在一個64位的作業系統中定義如下結構體: <span style="font-family:Microsoft YaHei;font-size:12px;">struct st_task { uint16_t id; uint32

linux C 結構struct的定義和初始

struct 定義: struct test { int a; long b; float c; char d; char e[]; }tt1,tt2;//物件宣告列表緊跟struct定義 struct test tt3,tt4;//單獨宣告物件列表 //宣告一個沒結構體名