dongyin6576 2016-11-04 11:12
浏览 81
已采纳

无效的内存地址或使用sql.DB的nil指针取消引用

I'm learning Go at the moment and trying to make a little SQL-toolset:

type DBUtils struct {
  User string
  Password string
  Host string
  Database string
  Handle *sql.DB
}

func (dbUtil DBUtils) Connect() {
  var err error
  dbUtil.Handle, err = sql.Open("mysql", dbUtil.User + ":" + dbUtil.Password + "@tcp(" + dbUtil.Host + ")/" + dbUtil.Database)
  if err != nil {
    panic(err.Error()) 
  }

  err = dbUtil.Handle.Ping()
  if err != nil {
    panic(err.Error())
  }

  fmt.Printf("%v", dbUtil)
}

func (dbUtil DBUtils) Close() {
  dbUtil.Handle.Close()
}

func (dbUtil DBUtils) GetString(what string, from string, where string, wherevalue string) string {
  var username string

  fmt.Printf("%v", dbUtil)

  stmtOut, err := dbUtil.Handle.Prepare("SELECT " + what + " FROM " + from + " WHERE " + where + " = " + wherevalue)
  if err != nil {
      panic(err.Error()) // proper error handling instead of panic in your app
  }

  err = stmtOut.QueryRow(1).Scan(&username)

  return username
}

So when using this with the following code:

db := databaseutils.DBUtils{"root", "root", "127.0.0.1:3306", "gotest", nil}    
db.Connect() // I get: {root root 127.0.0.1:3306 gotest 0xc42019d600}
fmt.Printf("%v", db) // I get {root root 127.0.0.1:3306 gotest <nil>}
x := db.GetString("username", "users", "id", "1") // Doesn't work: panic: runtime error: invalid memory address or nil pointer dereference
fmt.Println(x)

For me it seems like my DB handle isn't saved properly? Does anyone have an idea - I'm pretty new to go and there are many things looking different to PHP, JS, C++ etc.

Thanks in advance!

  • 写回答

2条回答 默认 最新

  • donglu5235 2016-11-04 15:10
    关注

    Your Connect method is not changing the state of the object you're calling the method on. You're working on a copy of the type. If you want a method to change the object itself, you'll have to define it on a pointer:

    func (dbUtil *DBUtils) Connect() {
    //instead of
    func (dbUtil DBUtils) Connect() {
    

    If you're familiar with C or C++, your current method works similarly to something like:

    void connect_db(struct db_utils db_util)
    {}
    

    When you call a function like that, you're creating a copy of the argument, and push that on to the stack. The connect_db function will work with that, and after it returns, the copy is deallocated.

    compare it to this C code:

    struct foo {
        int bar;
    };
    static
    void change_copy(struct foo bar)
    {
        bar.bar *= 2;
    }
    
    static
    void change_ptr(struct foo *bar)
    {
        bar->bar *= 2;
    }
    
    int main ( void )
    {
        struct foo bar = {10};
        printf("%d
    ", bar.bar);//prints 10
        change_copy(bar);//pass by value
        printf("%d
    ", bar.bar);//still prints 10
        change_ptr(&bar);
        printf("%d
    ", bar.bar);//prints 20
        return 0;
    }
    

    The same thing happens in go. The object on which you define the method can only change state of the instance if it has access to the instance. If not, it can't update that part of the memory.

    In case you're wondering, this method doesn't need to be defined on the pointer type:

    func (dbUtil DBUtils) Close() {
      dbUtil.Handle.Close()
    }
    

    The reason for this being that DBUtils.Handle is a pointer type. A copy of that pointer will always point to the same resource.

    I will say this, though: given that you're essentially wrapping the handle, and you're exposing the Connect and Close methods, the member itself really shouldn't be exported. I'd change it to lower-case handle

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(1条)

报告相同问题?

悬赏问题

  • ¥15 Vue3 大型图片数据拖动排序
  • ¥15 划分vlan后不通了
  • ¥15 GDI处理通道视频时总是带有白色锯齿
  • ¥20 用雷电模拟器安装百达屋apk一直闪退
  • ¥15 算能科技20240506咨询(拒绝大模型回答)
  • ¥15 自适应 AR 模型 参数估计Matlab程序
  • ¥100 角动量包络面如何用MATLAB绘制
  • ¥15 merge函数占用内存过大
  • ¥15 使用EMD去噪处理RML2016数据集时候的原理
  • ¥15 神经网络预测均方误差很小 但是图像上看着差别太大