C语言是一个比较底层的语言,似乎没有c++,java高级语言中对成员变量的保护,例如加上private修饰。但其实C语言也可以通过结构体的声明达到类型的效果。
一个实现的例子:
person_private.h
// person_private.h
#ifndef __PERSON_PRIVATE__
#define __PERSON_PRIVATE__struct person {char name[128];int age;char job[128];
};#endif
上述代码定义了一个私有的头文件,里面定义了结构person。
person.c
// person.c#include <stdio.h>
#include <malloc.h>
#include <string.h>
#include "person_private.h"struct person*
person_new(const char *name, int age, const char *job)
{if (!name || !job) return NULL;struct person *_this = (struct person *)calloc(1, sizeof(struct person));if (!_this) return NULL;memcpy(_this->name, name, strlen(name));_this->age = age;memcpy(_this->job, job, strlen(job));return _this;
}void
person_free(struct person **person)
{if (!person || !(*person)) return;free(*person);*person = NULL;
}void
getPersonInfo(struct person *person)
{if (!person) {printf("Error: param null pointer\n");return;}printf("name: %s, age = %d, job = %s\n", person->name, person->age, person->job);
person.c实现了对这个结构struct person分配内存,释放内存,和获取对象信息的函数。其中person_private.h只被person.c包含。
person.h
// person.h#ifndef __PERSON_H__
#define __PERSON_H__struct person;struct person*
person_new(const char *name, int age, const char *job);void
person_free(struct person **person);void
getPersonInfo(struct person *person);#endif
person.h这个头文件是一个“公共头文件”,被其他的应用程序include。里面声明了结构体struct person和公共函数。
main.c
// main.c
#include <stdio.h>
#include <malloc.h>
#include <string.h>
#include "person.h"int main()
{struct person *per = person_new("zhangsan", 20, "student");if (!per) return -1;getPersonInfo(per);person_free(&per);printf("After free, per = %p\n", per);return 0;
}
main.c实现了对结构struct person的应用。注意的是,在main.c中不能够直接定义一个struct person对象,只能定义struct person指针,是因为编译器不知道struct person真实占用的内存大小。仔细看main.c包含的是person.h,person.h里面只有结构体的声明。
在使用Sqlite时有以下简单的API:
Core Objects And Interfaces
The principal task of an SQL database engine is to evaluate SQL statements of SQL. To accomplish this, the developer needs two objects:
- The database connection object: sqlite3
- The prepared statement object: sqlite3_stmt
Strictly speaking, the prepared statement object is not required since the convenience wrapper interfaces, sqlite3_exec or sqlite3_get_table, can be used and these convenience wrappers encapsulate and hide the prepared statement object. Nevertheless, an understanding of prepared statements is needed to make full use of SQLite.
The database connection and prepared statement objects are controlled by a small set of C/C++ interface routine listed below.
- sqlite3_open()
- sqlite3_prepare()
- sqlite3_step()
- sqlite3_column()
- sqlite3_finalize()
- sqlite3_close()
Note that the list of routines above is conceptual rather than actual. Many of these routines come in multiple versions. For example, the list above shows a single routine named sqlite3_open() when in fact there are three separate routines that accomplish the same thing in slightly different ways: sqlite3_open(), sqlite3_open16() and sqlite3_open_v2(). The list mentions sqlite3_column() when in fact no such routine exists. The "sqlite3_column()" shown in the list is a placeholder for an entire family of routines that extra column data in various datatypes.
Here is a summary of what the core interfaces do:
-
sqlite3_open()
This routine opens a connection to an SQLite database file and returns a database connection object. This is often the first SQLite API call that an application makes and is a prerequisite for most other SQLite APIs. Many SQLite interfaces require a pointer to the database connection object as their first parameter and can be thought of as methods on the database connection object. This routine is the constructor for the database connection object.
-
sqlite3_prepare()
This routine converts SQL text into a prepared statement object and returns a pointer to that object. This interface requires a database connection pointer created by a prior call to sqlite3_open() and a text string containing the SQL statement to be prepared. This API does not actually evaluate the SQL statement. It merely prepares the SQL statement for evaluation.
Think of each SQL statement as a small computer program. The purpose of sqlite3_prepare() is to compile that program into object code. The prepared statement is the object code. The sqlite3_step() interface then runs the object code to get a result.
New applications should always invoke sqlite3_prepare_v2() instead of sqlite3_prepare(). The older sqlite3_prepare() is retained for backwards compatibility. But sqlite3_prepare_v2() provides a much better interface.
-
sqlite3_step()
This routine is used to evaluate a prepared statement that has been previously created by the sqlite3_prepare() interface. The statement is evaluated up to the point where the first row of results are available. To advance to the second row of results, invoke sqlite3_step() again. Continue invoking sqlite3_step() until the statement is complete. Statements that do not return results (ex: INSERT, UPDATE, or DELETE statements) run to completion on a single call to sqlite3_step().
-
sqlite3_column()
This routine returns a single column from the current row of a result set for a prepared statement that is being evaluated by sqlite3_step(). Each time sqlite3_step() stops with a new result set row, this routine can be called multiple times to find the values of all columns in that row.
As noted above, there really is no such thing as a "sqlite3_column()" function in the SQLite API. Instead, what we here call "sqlite3_column()" is a place-holder for an entire family of functions that return a value from the result set in various data types. There are also routines in this family that return the size of the result (if it is a string or BLOB) and the number of columns in the result set.
- sqlite3_column_blob()
- sqlite3_column_bytes()
- sqlite3_column_bytes16()
- sqlite3_column_count()
- sqlite3_column_double()
- sqlite3_column_int()
- sqlite3_column_int64()
- sqlite3_column_text()
- sqlite3_column_text16()
- sqlite3_column_type()
- sqlite3_column_value()
-
sqlite3_finalize()
This routine destroys a prepared statement created by a prior call to sqlite3_prepare(). Every prepared statement must be destroyed using a call to this routine in order to avoid memory leaks.
-
sqlite3_close()
This routine closes a database connection previously opened by a call to sqlite3_open(). All prepared statements associated with the connection should be finalized prior to closing the connection.
其中
int sqlite3_open(const char *filename, /* Database filename (UTF-8) */sqlite3 **ppDb /* OUT: SQLite db handle */
);
sqlite3为sqlite连接对象的数据结构,在API sqlite3_open()中使用sqlite3**ppDb二级指针作为参数:以下为解释:
sqlite3_open needs to somehow give you a database connection object; i.e. a sqlite3 object.
To prevent people from accessing the internals, sqlite3.h declares sqlite3 as an opaque struct. This means that you cannot allocate space for a sqlite3 since you don't know what it holds; the SQLite library must allocate it for you and give you a pointer to it (sqlite3*).
So now we have a hypothetical function sqlite3* sqlite3_open(...);, which opens a DB and returns a pointer to the connection object. But hold on; what if an error occurs? We could return NULL, but how is the developer supposed to distinguish a "database doesn't exist" error from a "database is read only", "database is corrupted", or other errors?
So instead, sqlite3_open returns an integer return code, and writes the connection pointer to the memory that the ppDB parameter points to if opening succeeded.
个人理解意思就是:改变一个内存中对象,需要他的地址作为参数达到在函数内部修改指针所指向的对象的值。但是sqlite3是一个不透明的结构,其中我们只能声明一个sqlite3的指针,然后调用sqlite3已经编译好了的库(类似printf()所在的stdio库)提供的方法,在方法的内部会分配sqlite3对象的空间并填充这个对象,并返回这个指针值给我们声明的指针。类似:
// function prototype (suppose sqlite3 lib provide us with a function called myVirtual_sqlite3_open() )
sqlite3 *myVirtual_sqlite3_open(const char *fileName); // create a database connection object
// Application code
sqlite3 *myConnectionObj = myVirtual_sqlite3_open("myDatabaseFileName.db");
这个函数会创建malloc一个sqlite3对象并填充他,然后返回这个指针给myConnectionObj,但是需要有不同的错误代码返回,所以需要把sqlite3连接对象作为参数返回,要修改参数只能实参到形参传递,形参要想传递给实参在C++中可以用引用,即:int sqlite3_open(const char * , sqlite3* &);
或者在C中用指针的指针,即int sqlite3_open(const char * , sqlite3 ** );这样两种方式才能达到修改sqlite3*的效果。
而
int sqlite3_prepare(sqlite3 *db, /* Database handle */const char *zSql, /* SQL statement, UTF-8 encoded */int nByte, /* Maximum length of zSql in bytes. */sqlite3_stmt **ppStmt, /* OUT: Statement handle */const char **pzTail /* OUT: Pointer to unused portion of zSql */
); 这个方法的实现由sqlite3库来实现,当然可以通过一级指针直接操作并修改sqlite3的数据结构。