c编程printf格式搜索表达式

时间:2015-08-01 09:34:40

标签: c format printf

我有一个小问题。 我有这段代码:

#include <stdio.h>

int main(){
    printf("%d, %f, %d\n", 0.9, 10, 'C');
}

输出是这样的:

10, 0.900000, 67

但我原以为:

0, 10.0, 67

但看起来printf在表达式中搜索相应的类型(int和float被转换) 任何人都可以帮我解决这个问题吗? 非常感谢你!

3 个答案:

答案 0 :(得分:2)

  

但我原以为:......

当格​​式字符串按顺序与参数类型不匹配时,行为未定义。任何事情都可能发生。你不能指望任何事情(我不明白为什么你会期望0.也许你希望printf使用格式字符串来转换浮点数和整数之间的参数。它只是没有。它是一个可变函数就像你可以编写自己的,并且格式字符串编码尾随参数的类型的事实不用于转换它们。)

现代调用约定对前几个参数使用寄存器,甚至可以使用寄存器来实现可变参数函数。在x86-64上,约定可以是例如可以在浮点寄存器xmm0中始终期望可变参数函数的第一个浮点参数,而整数参数在通用寄存器{{}中传递。 1}},%rdi%rsi,...这具有%rdx打印浮点参数后跟整数参数的效果。

作为一个例子,这是一个简短的程序:

printf("%f %d", 1, 1.0)

这就是我的编译器(Mac OS X 10.6上的Clang)编译程序的方法:

#include <stdio.h>

int main(){
  printf("%d, %f, %d\n", 0.9, 10, 'C');

  printf("%d, %f, %d\n", 10, 0.9, 'C');
}

很明显,这两个调用会产生相同的结果。但就一个而言,它是偶然的,只对这个特定的编译器版本和ABI“起作用”,而另一个尊重标准并且必须在任何地方工作。

同样,leaq <memory location of format string>, %rbx movq %rbx, %rdi movsd <memory location of 0.9 constant>, %xmm0 movl $10, %esi movl $67, %edx movb $1, %al callq _printf movq %rbx, %rdi movl $10, %esi movsd <memory location of 0.9 constant>, %xmm0 movl $67, %edx movb $1, %al callq _printf … 是未定义的行为,您不应在任何情况下使用它。

答案 1 :(得分:1)

唯一正确的期望是67使用%d格式说明符 * 打印字符。另外两个打印输出是未定义的行为。

  

看起来像printf在表达式中搜索相应的类型

这只是巧合。 printf不知道您传递的实际参数的类型。它信任格式字符串,并按顺序解释数据。您可以通过提供不同的数字来判断发生了什么,并观察输出的变化情况。

您看到的数字是垃圾 - double被重新解释为int,而int被重新解释为double。此外,如果doubleint的大小不同,前两个参数会互相交叉。

要生成您想要的输出,请为前两个参数表达式添加强制转换:

printf("%d, %f, %d\n", (int)0.9, (double)10, 'C');

请注意,您不需要强制转换最后一个参数,因为char会在处理int的变量长度参数列表时被提升为printf

* 只有当其他参数和格式说明符之间没有不匹配时,才会产生正确的行为。你的程序有UB,即使是最后一个参数,如果单独使用它也是正确的。

答案 2 :(得分:1)

这是未定义的行为,所以任何事情都可能发生。

import android.app.Activity; import android.content.ContentValues; import android.content.Context; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; public class MyDBHandler extends SQLiteOpenHelper { public static final int DATABASE_VERSION = 3; public static final String DATABASE_NAME = "favourites.db"; public static final String TABLE_FAVOURITES = "favourites"; public static final String COLUMN_ID = "_id"; public static final String COLUMN_VIDEONAME = "videoname"; public static final String COLUMN_LINK = "link"; public MyDBHandler(Context context, String name, String link, SQLiteDatabase.CursorFactory factory, int version) { super(context, DATABASE_NAME, factory, DATABASE_VERSION); } @Override public void onCreate(SQLiteDatabase db) { String query = "CREATE TABLE " + TABLE_FAVOURITES + "(" + COLUMN_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " + COLUMN_VIDEONAME + " TEXT, " + COLUMN_LINK + " TEXT " + ");"; db.execSQL(query); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { db.execSQL("DROP TABLE IF EXISTS " + TABLE_FAVOURITES); onCreate(db); } public void addFavourites(favourites favourites) { ContentValues values = new ContentValues(); values.put(COLUMN_VIDEONAME, favourites.get_videoname()); values.put(COLUMN_LINK, favourites.get_link()); SQLiteDatabase db = getWritableDatabase(); db.insert(TABLE_FAVOURITES, null,values); db.close(); } public void deleteFavourites(String videoName, String link) { SQLiteDatabase db = getWritableDatabase(); db.execSQL("DELETE FROM " + TABLE_FAVOURITES + " WHERE " + COLUMN_VIDEONAME + "=\"" + videoName + "\" " + COLUMN_LINK + "=\"" + link + "\" ;" ); } public String databaseToString() { String dbString = ""; SQLiteDatabase db = getWritableDatabase(); String query = "SELECT * FROM " + TABLE_FAVOURITES + " WHERE 1"; //Cursor points to a location in your results Cursor c = db.rawQuery(query, null); //Move to the first row in your results c.moveToFirst(); //Position after the last row means the end of the results while (!c.isAfterLast()) { if (c.getString(c.getColumnIndex("videoname")) != null) { dbString += c.getString(c.getColumnIndex("videoname")); dbString += "\n"; } c.moveToNext(); } db.close(); return dbString; } } 需要%d,但您传递的是int(而不是double),因此float占用{{1}的4个字节}}并将其解释为printf

double需要int,但您传递的是%f。因此它需要来自下一个内存的double和4个字节的4个字节,并将其解释为int

幸运的是传递16个字节,而int需要16个字节,所以最后一个值是正确的。